github.com/unionj-cloud/go-doudou/v2@v2.3.5/toolkit/pipeconn/pipe_listener.go (about) 1 // Copyright (c) 2020 StackRox Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License 14 15 package pipeconn 16 17 import ( 18 "context" 19 "errors" 20 "github.com/unionj-cloud/go-doudou/v2/toolkit/concurrency" 21 "net" 22 ) 23 24 const ( 25 // Network is the network reported by a pipe's address. 26 Network = "pipe" 27 ) 28 29 var ( 30 // ErrClosed indicates that a call to Accept() failed because the listener was closed 31 ErrClosed = errors.New("listener was closed") 32 33 // ErrAlreadyClosed indicates that a call to Close() failed because the listener had already been closed. 34 ErrAlreadyClosed = errors.New("already closed") 35 36 pipeAddr = func() net.Addr { 37 c1, c2 := net.Pipe() 38 addr := c1.RemoteAddr() 39 _ = c1.Close() 40 _ = c2.Close() 41 return addr 42 }() 43 ) 44 45 // DialContextFunc is a function for dialing a pipe listener. 46 type DialContextFunc func(context.Context) (net.Conn, error) 47 48 type pipeListener struct { 49 closed concurrency.Signal 50 serverConnsC chan net.Conn 51 } 52 53 // NewPipeListener returns a net.Listener that accepts connections which are local pipe connections (i.e., via 54 // net.Pipe()). It also returns a function that implements a context-aware dial. 55 func NewPipeListener() (net.Listener, DialContextFunc) { 56 lis := &pipeListener{ 57 closed: concurrency.NewSignal(), 58 serverConnsC: make(chan net.Conn), 59 } 60 61 return lis, lis.DialContext 62 } 63 64 func (l *pipeListener) Accept() (net.Conn, error) { 65 if l.closed.IsDone() { 66 return nil, ErrClosed 67 } 68 select { 69 case conn := <-l.serverConnsC: 70 return conn, nil 71 case <-l.closed.Done(): 72 return nil, ErrClosed 73 } 74 } 75 76 func (l *pipeListener) DialContext(ctx context.Context) (net.Conn, error) { 77 if l.closed.IsDone() { 78 return nil, ErrClosed 79 } 80 81 serverConn, clientConn := net.Pipe() 82 83 select { 84 case l.serverConnsC <- serverConn: 85 return clientConn, nil 86 case <-l.closed.Done(): 87 return nil, ErrClosed 88 case <-ctx.Done(): 89 return nil, ctx.Err() 90 } 91 } 92 93 func (l *pipeListener) Addr() net.Addr { 94 return pipeAddr 95 } 96 97 func (l *pipeListener) Close() error { 98 if !l.closed.Signal() { 99 return ErrAlreadyClosed 100 } 101 return nil 102 }