// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

// Package connctx wraps net.Conn using context.Context. // // Deprecated: use netctx instead.
package connctx import ( ) // ErrClosing is returned on Write to closed connection. var ErrClosing = errors.New("use of closed network connection") // Reader is an interface for context controlled reader. type Reader interface { ReadContext(context.Context, []byte) (int, error) } // Writer is an interface for context controlled writer. type Writer interface { WriteContext(context.Context, []byte) (int, error) } // ReadWriter is a composite of ReadWriter. type ReadWriter interface { Reader Writer } // ConnCtx is a wrapper of net.Conn using context.Context. type ConnCtx interface { Reader Writer io.Closer LocalAddr() net.Addr RemoteAddr() net.Addr Conn() net.Conn } type connCtx struct { nextConn net.Conn closed chan struct{} closeOnce sync.Once readMu sync.Mutex writeMu sync.Mutex } var veryOld = time.Unix(0, 1) //nolint:gochecknoglobals // New creates a new ConnCtx wrapping given net.Conn. func ( net.Conn) ConnCtx { := &connCtx{ nextConn: , closed: make(chan struct{}), } return } func ( *connCtx) ( context.Context, []byte) (int, error) { .readMu.Lock() defer .readMu.Unlock() select { case <-.closed: return 0, io.EOF default: } := make(chan struct{}) var sync.WaitGroup var atomic.Value .Add(1) go func() { defer .Done() select { case <-.Done(): // context canceled if := .nextConn.SetReadDeadline(veryOld); != nil { .Store() return } <- if := .nextConn.SetReadDeadline(time.Time{}); != nil { .Store() } case <-: } }() , := .nextConn.Read() close() .Wait() if := .Err(); != nil && == 0 { = } if , := .Load().(error); && == nil && != nil { = } return , } func ( *connCtx) ( context.Context, []byte) (int, error) { .writeMu.Lock() defer .writeMu.Unlock() select { case <-.closed: return 0, ErrClosing default: } := make(chan struct{}) var sync.WaitGroup var atomic.Value .Add(1) go func() { defer .Done() select { case <-.Done(): // context canceled if := .nextConn.SetWriteDeadline(veryOld); != nil { .Store() return } <- if := .nextConn.SetWriteDeadline(time.Time{}); != nil { .Store() } case <-: } }() , := .nextConn.Write() close() .Wait() if := .Err(); != nil && == 0 { = } if , := .Load().(error); && == nil && != nil { = } return , } func ( *connCtx) () error { := .nextConn.Close() .closeOnce.Do(func() { .writeMu.Lock() .readMu.Lock() close(.closed) .readMu.Unlock() .writeMu.Unlock() }) return } func ( *connCtx) () net.Addr { return .nextConn.LocalAddr() } func ( *connCtx) () net.Addr { return .nextConn.RemoteAddr() } func ( *connCtx) () net.Conn { return .nextConn }