aboutsummaryrefslogtreecommitdiff
path: root/net.go
diff options
context:
space:
mode:
Diffstat (limited to 'net.go')
-rw-r--r--net.go88
1 files changed, 88 insertions, 0 deletions
diff --git a/net.go b/net.go
new file mode 100644
index 0000000..b1331e9
--- /dev/null
+++ b/net.go
@@ -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" }