Source File
netchan.go
Belonging Package
go.pact.im/x/netchan
// Package netchan provides an in-memory implementation of the [net.Listener]
// interface, allowing programs to simulate network connections using channels
// and pipes instead of real network sockets.
package netchan
import (
)
var _ net.Listener = (*Listener)(nil)
// ErrClosed indicates that the [Listener] has been closed and no further
// operations can be performed.
var ErrClosed = errors.New("netchan: closed")
// chanAddr implements the [net.chanAddr] interface representing the network
// address of an in-memory [Listener]. Since connections are in-memory, chanAddr
// returns constant values indicating the connection uses channels.
type chanAddr struct{}
// Network returns the name of the network, which is always "chan".
func (chanAddr) () string { return "chan" }
// String returns a string representation of the Addr, which is always "chan".
func (chanAddr) () string { return "chan" }
// Listener implements the [net.Listener] interface using channels for
// accepting connections. Incoming connections are sent on an internal
// channel and accepted by Accept calls. The Listener can be closed to
// unblock Accept calls and signal shutdown.
type Listener struct {
addr net.Addr
conn chan net.Conn
done chan struct{}
once sync.Once
}
// NewListener returns a new Listener with the default address.
func () *Listener {
return NewListenerAddr(chanAddr{})
}
// NewListenerAddr returns a new Listener instance with the specified network
// address.
func ( net.Addr) *Listener {
return &Listener{
addr: ,
conn: make(chan net.Conn),
done: make(chan struct{}),
}
}
// Accept waits for and returns the next connection sent to the listener’s
// connection channel. If the listener is closed, Accept returns [ErrClosed].
func ( *Listener) () (net.Conn, error) {
select {
// Accept should return an error after Close.
case <-.done:
return nil, ErrClosed
default:
}
:
select {
case , := <-.conn:
// Handle unlikely case of close(l.C()).
if ! {
return nil, ErrClosed
}
// Allow sending nil connection to wait for Accept call.
if == nil {
goto
}
return , nil
// Unblock Accept when Close is called.
case <-.done:
return nil, ErrClosed
}
}
// Addr implements the [net.Listener] interface.
func ( *Listener) () net.Addr {
return chanAddr{}
}
// Close closes the listener and unblocks all Accept calls.
func ( *Listener) () error {
.once.Do(func() { close(.done) })
return nil
}
// C returns the internal channel used to send [net.Conn] connections to the
// Accept callers. The returned channel should not be closed; use Close method
// to unblock Accept instead.
//
// Sending a nil connection on this channel is allowed and can be used
// to wait for the Accept caller to be ready.
func ( *Listener) () chan net.Conn {
return .conn
}
// Done returns a channel that is closed when the Listener is closed.
func ( *Listener) () <-chan struct{} {
return .done
}
// Dial creates a new in-memory connection pair using net.Pipe and sends the
// server side connection to the Listener’s internal connection channel.
//
// It returns the client side connection, which can be used to communicate
// with the accepted connection on the Listener side.
//
// Dial blocks until the server side connection is accepted by a call to Accept,
// or until the Listener is closed or the provided context is canceled.
func ( *Listener) ( context.Context) (net.Conn, error) {
, := net.Pipe()
select {
case .conn <- :
return , nil
case <-.done:
return nil, ErrClosed
case <-.Done():
return nil, .Err()
}
}
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64)