package connutil import ( "errors" "net" "os" "time" "github.com/anyproto/any-sync/app/logger" "go.uber.org/zap" ) var log = logger.NewNamed("common.net.connutil") type TimeoutConn struct { net.Conn timeout time.Duration } func NewTimeout(conn net.Conn, timeout time.Duration) *TimeoutConn { return &TimeoutConn{conn, timeout} } func (c *TimeoutConn) Write(p []byte) (n int, err error) { for { if c.timeout != 0 { if e := c.Conn.SetWriteDeadline(time.Now().Add(c.timeout)); e != nil { log.Warn("can't set write deadline", zap.String("remoteAddr", c.RemoteAddr().String())) } } nn, err := c.Conn.Write(p[n:]) n += nn if n < len(p) && nn > 0 && errors.Is(err, os.ErrDeadlineExceeded) { // Keep extending the deadline so long as we're making progress. log.Debug("keep extending the deadline so long as we're making progress", zap.String("remoteAddr", c.RemoteAddr().String())) continue } if c.timeout != 0 { if e := c.Conn.SetWriteDeadline(time.Time{}); e != nil { log.Warn("can't set write deadline", zap.String("remoteAddr", c.RemoteAddr().String())) } } if err != nil { // if the connection is timed out and we should close it if e := c.Conn.Close(); e != nil { log.Warn("connection close error", zap.String("remoteAddr", c.RemoteAddr().String())) } log.Debug("connection timed out", zap.String("remoteAddr", c.RemoteAddr().String())) } return n, err } }