diff options
| author | Grégoire Duchêne <gduchene@awhk.org> | 2022-06-05 18:01:39 +0100 |
|---|---|---|
| committer | Grégoire Duchêne <gduchene@awhk.org> | 2022-06-05 18:01:39 +0100 |
| commit | 0315338ef5c5fadd739088684323b82535fc904b (patch) | |
| tree | c7febbf73ef122cd7c46f3e6e81c09432c8d4987 /net.go | |
| parent | 326a741c1298959e5d6e5eb1a6b2225ef9151063 (diff) | |
Add Listen, Must, PipeListener, and T
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" } |
