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  }