// Copyright 2026 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package nettestimport ()// Conn is an in-memory test implementation of net.Conn.typeConnstruct {// Conns come in pairs. // Writes to one Conn are read by its peer, and vice-versa. // // A connHalf handles one direction of data flow. // A Conn consists of read and write halves. // A Conn's peer has the same halves, only swapped. // // A Conn reads from r and writes to w. r, w *connHalf// peer is the other endpoint. peer *Conn}// NewConnPair returns a pair of connected Conns.func () (*Conn, *Conn) {returnnewConnPair(net.TCPAddrFromAddrPort(netip.MustParseAddrPort("192.0.2.1:10000")),net.TCPAddrFromAddrPort(netip.MustParseAddrPort("192.0.2.1:10001")), )}func newConnPair(, net.Addr) (*Conn, *Conn) { := newConnHalf() := newConnHalf() := &Conn{r: , w: } := &Conn{r: , w: } .peer = .peer = .SetReadBufferSize(-1) .SetReadBufferSize(-1)return , }// Peer returns the other end of the connection.func ( *Conn) () *Conn {return .peer}// Read reads data from the connection.func ( *Conn) ( []byte) ( int, error) { , = .r.read()if != nil && != io.EOF { = &net.OpError{Op: "read",Net: "tcp",Source: .RemoteAddr(),Addr: .LocalAddr(),Err: , } }return , }// CanRead reports whether Read can proceed without blocking.func ( *Conn) () bool {return .r.canRead()}// Write writes data to the connection.func ( *Conn) ( []byte) ( int, error) { , = .w.write()if != nil { = &net.OpError{Op: "write",Net: "tcp",Source: .LocalAddr(),Addr: .RemoteAddr(),Err: , } }return , }// IsClosed reports whether the connection has been closed.// A connection is closed if [CloseRead] and [CloseWrite] are both called,// or if [Close] is called.//// To identify when the other side of the Conn has been closed,// use Conn.Peer().IsClosed().func ( *Conn) () bool { .r.lock() := .r.readClosed .r.unlock() .w.lock() := .w.writeClosed .w.unlock()return && }var errClosedByPeer = errors.New("connection closed by peer")// CloseRead shuts down the reading side of the connection.func ( *Conn) () error { .r.lock()defer .r.unlock() .r.buf.Reset() // discard unread data .r.readClosed = truereturnnil}// CloseWrite shuts down the writing side of the connection.func ( *Conn) () error { .w.lock()defer .w.unlock() .w.writeClosed = truereturnnil}// Close closes the connection.func ( *Conn) () error { .r.lock() := .r.readClosed .r.buf.Reset() // discard unread data .r.readClosed = true := .r.closeErr .r.unlock() .w.lock() := .w.writeClosed .w.writeClosed = true .w.unlock()if && { = net.ErrClosed }if != nil { = &net.OpError{Op: "close",Net: "tcp",Addr: .LocalAddr(),Err: , } }return}// SetCloseError sets the error returned by Close.// Close still closes the connection.// A nil error restores the usual behavior.func ( *Conn) ( error) { .r.lock() .r.closeErr = .r.unlock()}// LocalAddr returns the (fake) local network address.func ( *Conn) () net.Addr { .r.lock()defer .r.unlock()return .r.addr}// SetLocalAddr sets the local address.//// To set the remote address, set the local address of Conn's peer.func ( *Conn) ( net.Addr) { .r.lock()defer .r.unlock() .r.addr = }// LocalAddr returns the (fake) remote network address.func ( *Conn) () net.Addr { .r.lock()defer .r.unlock()return .w.addr}// SetDeadline sets the read and write deadlines for the connection.func ( *Conn) ( time.Time) error { .SetReadDeadline() .SetWriteDeadline()returnnil}// SetReadDeadline sets the read deadline for the connection.func ( *Conn) ( time.Time) error { .r.readDeadline.setDeadline(.r, )returnnil}// SetWriteDeadline sets the write deadline for the connection.func ( *Conn) ( time.Time) error { .w.writeDeadline.setDeadline(.w, )returnnil}// SetReadBufferSize sets the connection's read buffer.// Writes to the other end of the connection will block so long as the buffer is full.// Setting the size to 0 blocks all writes until the size is increased.func ( *Conn) ( int) {if < 0 { = math.MaxInt } .r.setBufferSize()}// SetReadError causes any currently blocked and future Read calls to return// a net.OpError wrapping err. It does not affect the other half of the connection.// Reads will return any buffered data before returning the error,// including data written after the error is set and io.EOF after the other end is closed.// A nil error restores the usual behavior.func ( *Conn) ( error) { .r.lock()defer .r.unlock() .r.readErr = }// SetWriteError causes any currently blocked and future Write calls to return// a net.OpError wrapping err. It does not affect the other half of the connection.// Writes will not write data to the connection buffer while an error is set.// A nil error restores the usual behavior.func ( *Conn) ( error) { .w.lock()defer .w.unlock() .w.writeErr = }// connHalf is one direction data flow in a Conn.// The connHalf contains a buffer.// Writes to the connHalf push to the buffer and reads pull from it.type connHalf struct { addr net.Addr// A half can be readable and/or writable. // // These four channels act as a lock, // and allow waiting for readability/writability. // When the half is unlocked, exactly one channel contains a value. // When the half is locked, all channels are empty. lockr chanstruct{} // readable lockw chanstruct{} // writable lockrw chanstruct{} // readable and writable lockc chanstruct{} // neither readable nor writable// Read and write timeouts. readDeadline, writeDeadline connDeadline bufMax int// maximum buffer size buf bytes.Buffer readClosed, writeClosed bool readErr, writeErr error// errors returned by reads/writes closeErr error// error returned by closing the conn reading from this half}func newConnHalf( net.Addr) *connHalf { := &connHalf{addr: ,lockw: make(chanstruct{}, 1),lockr: make(chanstruct{}, 1),lockrw: make(chanstruct{}, 1),lockc: make(chanstruct{}, 1),bufMax: math.MaxInt, // unlimited } .unlock()return}// lock locks h.func ( *connHalf) () {select {case<-.lockw: // writablecase<-.lockr: // readablecase<-.lockrw: // readable and writablecase<-.lockc: // neither readable nor writable }}// unlock unlocks h.func ( *connHalf) () { := .canReadLocked() := .canWriteLocked()switch {case && : .lockrw <- struct{}{} // readable and writablecase : .lockr <- struct{}{} // readablecase : .lockw <- struct{}{} // writabledefault: .lockc <- struct{}{} // neither readable nor writable }}func ( *connHalf) () bool { .lock()defer .unlock()return .canReadLocked()}func ( *connHalf) () bool {return .readErr != nil || .readDeadline.expired || .buf.Len() > 0 || .readClosed || .writeClosed}func ( *connHalf) () bool {return .writeErr != nil || .writeDeadline.expired || .bufMax > .buf.Len() || .readClosed || .writeClosed}// waitAndLockForRead waits until h is readable and locks it.func ( *connHalf) () {select {case<-.lockr:// readablecase<-.lockrw:// readable and writable }}// waitAndLockForWrite waits until h is writable and locks it.func ( *connHalf) () {select {case<-.lockw:// writablecase<-.lockrw:// readable and writable }}func ( *connHalf) ( []byte) ( int, error) { .waitAndLockForRead()defer .unlock()if .readClosed {return0, net.ErrClosed }if .readDeadline.expired {return0, os.ErrDeadlineExceeded }if .buf.Len() > 0 {return .buf.Read() }if .writeClosed {return0, io.EOF }return0, .readErr}func ( *connHalf) ( int) { .lock()defer .unlock() .bufMax = }func ( *connHalf) ( []byte) ( int, error) {for < len() { , := .writePartial([:]) += if != nil {return , } }return , nil}func ( *connHalf) ( []byte) ( int, error) { .waitAndLockForWrite()defer .unlock()if .writeClosed {return0, net.ErrClosed }if .writeDeadline.expired {return0, os.ErrDeadlineExceeded }if .readClosed {return0, errClosedByPeer }if .writeErr != nil {return0, .writeErr } := .bufMax - .buf.Len()if < len() { = [:] }return .buf.Write()}type connDeadline struct { timer *time.Timer expired bool}type locker interface { lock() unlock()}func ( *connDeadline) ( locker, time.Time) { .lock()defer .unlock()if .timer != nil { .timer.Stop() .timer = nil }if .IsZero() {// No deadline. .expired = falsereturn } := time.Until()if <= 0 {// Deadline has already passed. .expired = truereturn }// Deadline is in the future. .expired = falsevar *time.Timer = time.AfterFunc(, func() { .lock()defer .unlock()if .timer == { .timer = nil .expired = true } }) .timer = }
The pages are generated with Goldsv0.8.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.