diff options
Diffstat (limited to 'net.go')
| -rw-r--r-- | net.go | 88 |
1 files changed, 88 insertions, 0 deletions
@@ -0,0 +1,88 @@ +package core + +import ( + "context" + "net" + "strings" + "sync" + "syscall" +) + +// Listen is a wrapper around net.Listen. If addr cannot be split in two +// parts around the first colon found, Listen will try to create a UNIX +// or TCP net.Listener depending on whether addr contains a slash. +func Listen(addr string) (net.Listener, error) { + if fields := strings.SplitN(addr, ":", 2); len(fields) == 2 { + return net.Listen(fields[0], fields[1]) + } + if strings.ContainsRune(addr, '/') { + return net.Listen("unix", addr) + } + return net.Listen("tcp", addr) +} + +// PipeListener is a net.Listener that works over a pipe. It provides +// dialer functions that can be used in an HTTP client or gRPC options. +// +// Its zero value is safe to use. PipeListener must not be copied after +// its first use. +type PipeListener struct { + conns chan net.Conn + done chan struct{} + + closeOnce sync.Once + initOnce sync.Once +} + +var _ net.Listener = &PipeListener{} + +func (p *PipeListener) Accept() (net.Conn, error) { + p.initOnce.Do(p.init) + + select { + case conn := <-p.conns: + return conn, nil + case <-p.done: + return nil, syscall.EINVAL + } +} + +func (p *PipeListener) Addr() net.Addr { return pipeListenerAddr{} } + +func (p *PipeListener) Close() error { + p.initOnce.Do(p.init) + p.closeOnce.Do(func() { close(p.done) }) + return nil +} + +func (p *PipeListener) Dial(_, _ string) (net.Conn, error) { + return p.DialContext(context.Background(), "", "") +} + +func (p *PipeListener) DialContext(ctx context.Context, _, _ string) (net.Conn, error) { + p.initOnce.Do(p.init) + + s, c := net.Pipe() + select { + case p.conns <- s: + return c, nil + case <-p.done: + return nil, syscall.ECONNREFUSED + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func (p *PipeListener) DialContextGRPC(ctx context.Context, _ string) (net.Conn, error) { + return p.DialContext(ctx, "", "") +} + +func (p *PipeListener) init() { + p.conns = make(chan net.Conn) + p.done = make(chan struct{}) +} + +type pipeListenerAddr struct{} + +func (pipeListenerAddr) Network() string { return "pipe" } +func (pipeListenerAddr) String() string { return "pipe" } |
