1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
package grpctool
import (
"context"
"errors"
"net"
"sync"
)
type DialListener struct {
closeOnce sync.Once
done chan struct{}
pipe chan net.Conn
}
func NewDialListener() *DialListener {
return &DialListener{
done: make(chan struct{}),
pipe: make(chan net.Conn),
}
}
func (l *DialListener) Accept() (net.Conn, error) {
select {
case <-l.done:
return nil, errors.New("listener closed, cannot accept")
default:
}
select {
case <-l.done:
return nil, errors.New("listener closed, cannot accept")
case conn := <-l.pipe:
return conn, nil
}
}
func (l *DialListener) Close() error {
l.closeOnce.Do(func() {
close(l.done)
})
return nil
}
func (l *DialListener) Addr() net.Addr {
return pipeAddr{}
}
func (l *DialListener) DialContext(ctx context.Context, addr string) (net.Conn, error) {
// When multiple channels are ready, 'select' operator picks one of them, but which one is not defined.
// But we want to guarantee that once the listener had been closed it will never return a connection so
// we check here that it's still not closed before the main 'select'.
select {
case <-l.done:
return nil, errors.New("listener closed, cannot dial")
default:
}
p1, p2 := net.Pipe()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-l.done:
return nil, errors.New("listener closed, cannot dial")
case l.pipe <- p1:
return p2, nil
}
}
type pipeAddr struct{}
func (pipeAddr) Network() string {
return "pipe"
}
func (pipeAddr) String() string {
return "pipe"
}
|