github.com/lfch/etcd-io/tests/v3@v3.0.0-20221004140520-eac99acd3e9d/framework/integration/bridge.go (about) 1 // Copyright 2016 The etcd Authors 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 integration 16 17 import ( 18 "io" 19 "net" 20 "sync" 21 ) 22 23 type Dialer interface { 24 Dial() (net.Conn, error) 25 } 26 27 // bridge proxies connections between listener and dialer, making it possible 28 // to disconnect grpc network connections without closing the logical grpc connection. 29 type bridge struct { 30 dialer Dialer 31 l net.Listener 32 conns map[*bridgeConn]struct{} 33 34 stopc chan struct{} 35 pausec chan struct{} 36 blackholec chan struct{} 37 wg sync.WaitGroup 38 39 mu sync.Mutex 40 } 41 42 func newBridge(dialer Dialer, listener net.Listener) (*bridge, error) { 43 b := &bridge{ 44 // bridge "port" is ("%05d%05d0", port, pid) since go1.8 expects the port to be a number 45 dialer: dialer, 46 l: listener, 47 conns: make(map[*bridgeConn]struct{}), 48 stopc: make(chan struct{}), 49 pausec: make(chan struct{}), 50 blackholec: make(chan struct{}), 51 } 52 close(b.pausec) 53 b.wg.Add(1) 54 go b.serveListen() 55 return b, nil 56 } 57 58 func (b *bridge) Close() { 59 b.l.Close() 60 b.mu.Lock() 61 select { 62 case <-b.stopc: 63 default: 64 close(b.stopc) 65 } 66 b.mu.Unlock() 67 b.wg.Wait() 68 } 69 70 func (b *bridge) DropConnections() { 71 b.mu.Lock() 72 defer b.mu.Unlock() 73 for bc := range b.conns { 74 bc.Close() 75 } 76 b.conns = make(map[*bridgeConn]struct{}) 77 } 78 79 func (b *bridge) PauseConnections() { 80 b.mu.Lock() 81 b.pausec = make(chan struct{}) 82 b.mu.Unlock() 83 } 84 85 func (b *bridge) UnpauseConnections() { 86 b.mu.Lock() 87 select { 88 case <-b.pausec: 89 default: 90 close(b.pausec) 91 } 92 b.mu.Unlock() 93 } 94 95 func (b *bridge) serveListen() { 96 defer func() { 97 b.l.Close() 98 b.mu.Lock() 99 for bc := range b.conns { 100 bc.Close() 101 } 102 b.mu.Unlock() 103 b.wg.Done() 104 }() 105 106 for { 107 inc, ierr := b.l.Accept() 108 if ierr != nil { 109 return 110 } 111 b.mu.Lock() 112 pausec := b.pausec 113 b.mu.Unlock() 114 select { 115 case <-b.stopc: 116 inc.Close() 117 return 118 case <-pausec: 119 } 120 121 outc, oerr := b.dialer.Dial() 122 if oerr != nil { 123 inc.Close() 124 return 125 } 126 127 bc := &bridgeConn{inc, outc, make(chan struct{})} 128 b.wg.Add(1) 129 b.mu.Lock() 130 b.conns[bc] = struct{}{} 131 go b.serveConn(bc) 132 b.mu.Unlock() 133 } 134 } 135 136 func (b *bridge) serveConn(bc *bridgeConn) { 137 defer func() { 138 close(bc.donec) 139 bc.Close() 140 b.mu.Lock() 141 delete(b.conns, bc) 142 b.mu.Unlock() 143 b.wg.Done() 144 }() 145 146 var wg sync.WaitGroup 147 wg.Add(2) 148 go func() { 149 b.ioCopy(bc.out, bc.in) 150 bc.close() 151 wg.Done() 152 }() 153 go func() { 154 b.ioCopy(bc.in, bc.out) 155 bc.close() 156 wg.Done() 157 }() 158 wg.Wait() 159 } 160 161 type bridgeConn struct { 162 in net.Conn 163 out net.Conn 164 donec chan struct{} 165 } 166 167 func (bc *bridgeConn) Close() { 168 bc.close() 169 <-bc.donec 170 } 171 172 func (bc *bridgeConn) close() { 173 bc.in.Close() 174 bc.out.Close() 175 } 176 177 func (b *bridge) Blackhole() { 178 b.mu.Lock() 179 close(b.blackholec) 180 b.mu.Unlock() 181 } 182 183 func (b *bridge) Unblackhole() { 184 b.mu.Lock() 185 for bc := range b.conns { 186 bc.Close() 187 } 188 b.conns = make(map[*bridgeConn]struct{}) 189 b.blackholec = make(chan struct{}) 190 b.mu.Unlock() 191 } 192 193 // ref. https://github.com/golang/go/blob/master/src/io/io.go copyBuffer 194 func (b *bridge) ioCopy(dst io.Writer, src io.Reader) (err error) { 195 buf := make([]byte, 32*1024) 196 for { 197 select { 198 case <-b.blackholec: 199 io.Copy(io.Discard, src) 200 return nil 201 default: 202 } 203 nr, er := src.Read(buf) 204 if nr > 0 { 205 nw, ew := dst.Write(buf[0:nr]) 206 if ew != nil { 207 return ew 208 } 209 if nr != nw { 210 return io.ErrShortWrite 211 } 212 } 213 if er != nil { 214 err = er 215 break 216 } 217 } 218 return err 219 }