github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/integration/resources/docker/dockerexternal/etcdintegration/bridge/bridge.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 // Copyright 2016 The etcd Authors 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package bridge 36 37 import ( 38 "io" 39 "net" 40 "sync" 41 ) 42 43 // Dialer makes TCP connections. 44 type Dialer interface { 45 Dial() (net.Conn, error) 46 } 47 48 // Bridge proxies connections between listener and dialer, making it possible 49 // to disconnect grpc network connections without closing the logical grpc connection. 50 type Bridge struct { 51 dialer Dialer 52 l net.Listener 53 conns map[*bridgeConn]struct{} 54 55 stopc chan struct{} 56 pausec chan struct{} 57 blackholec chan struct{} 58 wg sync.WaitGroup 59 60 mu sync.Mutex 61 } 62 63 // New constructs a bridge listening to the given listener and connecting using the given dialer. 64 func New(dialer Dialer, listener net.Listener) (*Bridge, error) { 65 b := &Bridge{ 66 // Bridge "port" is ("%05d%05d0", port, pid) since go1.8 expects the port to be a number 67 dialer: dialer, 68 l: listener, 69 conns: make(map[*bridgeConn]struct{}), 70 stopc: make(chan struct{}), 71 pausec: make(chan struct{}), 72 blackholec: make(chan struct{}), 73 } 74 close(b.pausec) 75 b.wg.Add(1) 76 go b.serveListen() 77 return b, nil 78 } 79 80 // Close stops the bridge. 81 func (b *Bridge) Close() { 82 //nolint:errcheck 83 b.l.Close() 84 b.mu.Lock() 85 select { 86 case <-b.stopc: 87 default: 88 close(b.stopc) 89 } 90 b.mu.Unlock() 91 b.wg.Wait() 92 } 93 94 // DropConnections drops connections to the bridge. 95 func (b *Bridge) DropConnections() { 96 b.mu.Lock() 97 defer b.mu.Unlock() 98 for bc := range b.conns { 99 bc.Close() 100 } 101 b.conns = make(map[*bridgeConn]struct{}) 102 } 103 104 // PauseConnections pauses all connections. 105 func (b *Bridge) PauseConnections() { 106 b.mu.Lock() 107 b.pausec = make(chan struct{}) 108 b.mu.Unlock() 109 } 110 111 // UnpauseConnections unpauses all connections. 112 func (b *Bridge) UnpauseConnections() { 113 b.mu.Lock() 114 select { 115 case <-b.pausec: 116 default: 117 close(b.pausec) 118 } 119 b.mu.Unlock() 120 } 121 122 func (b *Bridge) serveListen() { 123 defer func() { 124 //nolint:errcheck 125 b.l.Close() 126 b.mu.Lock() 127 for bc := range b.conns { 128 bc.Close() 129 } 130 b.mu.Unlock() 131 b.wg.Done() 132 }() 133 134 for { 135 inc, ierr := b.l.Accept() 136 if ierr != nil { 137 return 138 } 139 b.mu.Lock() 140 pausec := b.pausec 141 b.mu.Unlock() 142 select { 143 case <-b.stopc: 144 //nolint:errcheck 145 inc.Close() 146 return 147 case <-pausec: 148 } 149 150 outc, oerr := b.dialer.Dial() 151 if oerr != nil { 152 //nolint:errcheck 153 inc.Close() 154 return 155 } 156 157 bc := &bridgeConn{inc, outc, make(chan struct{})} 158 b.wg.Add(1) 159 b.mu.Lock() 160 b.conns[bc] = struct{}{} 161 go b.serveConn(bc) 162 b.mu.Unlock() 163 } 164 } 165 166 func (b *Bridge) serveConn(bc *bridgeConn) { 167 defer func() { 168 close(bc.donec) 169 bc.Close() 170 b.mu.Lock() 171 delete(b.conns, bc) 172 b.mu.Unlock() 173 b.wg.Done() 174 }() 175 176 var wg sync.WaitGroup 177 wg.Add(2) 178 go func() { 179 //nolint:errcheck 180 b.ioCopy(bc.out, bc.in) 181 bc.close() 182 wg.Done() 183 }() 184 go func() { 185 //nolint:errcheck 186 b.ioCopy(bc.in, bc.out) 187 bc.close() 188 wg.Done() 189 }() 190 wg.Wait() 191 } 192 193 type bridgeConn struct { 194 in net.Conn 195 out net.Conn 196 donec chan struct{} 197 } 198 199 func (bc *bridgeConn) Close() { 200 bc.close() 201 <-bc.donec 202 } 203 204 func (bc *bridgeConn) close() { 205 //nolint:errcheck 206 bc.in.Close() 207 //nolint:errcheck 208 bc.out.Close() 209 } 210 211 // Blackhole stops connections to the bridge. 212 func (b *Bridge) Blackhole() { 213 b.mu.Lock() 214 close(b.blackholec) 215 b.mu.Unlock() 216 } 217 218 // Unblackhole stops connections to the bridge. 219 func (b *Bridge) Unblackhole() { 220 b.mu.Lock() 221 for bc := range b.conns { 222 bc.Close() 223 } 224 b.conns = make(map[*bridgeConn]struct{}) 225 b.blackholec = make(chan struct{}) 226 b.mu.Unlock() 227 } 228 229 // ref. https://github.com/golang/go/blob/master/src/io/io.go copyBuffer 230 func (b *Bridge) ioCopy(dst io.Writer, src io.Reader) (err error) { 231 buf := make([]byte, 32*1024) 232 for { 233 select { 234 case <-b.blackholec: 235 //nolint:errcheck 236 io.Copy(io.Discard, src) 237 return nil 238 default: 239 } 240 nr, er := src.Read(buf) 241 if nr > 0 { 242 nw, ew := dst.Write(buf[0:nr]) 243 if ew != nil { 244 return ew 245 } 246 if nr != nw { 247 return io.ErrShortWrite 248 } 249 } 250 if er != nil { 251 err = er 252 break 253 } 254 } 255 return err 256 }