github.com/v2fly/v2ray-core/v4@v4.45.2/app/commander/outbound.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package commander
     5  
     6  import (
     7  	"context"
     8  	"sync"
     9  
    10  	"github.com/v2fly/v2ray-core/v4/common"
    11  	"github.com/v2fly/v2ray-core/v4/common/net"
    12  	"github.com/v2fly/v2ray-core/v4/common/signal/done"
    13  	"github.com/v2fly/v2ray-core/v4/transport"
    14  )
    15  
    16  // OutboundListener is a net.Listener for listening gRPC connections.
    17  type OutboundListener struct {
    18  	buffer chan net.Conn
    19  	done   *done.Instance
    20  }
    21  
    22  func (l *OutboundListener) add(conn net.Conn) {
    23  	select {
    24  	case l.buffer <- conn:
    25  	case <-l.done.Wait():
    26  		conn.Close()
    27  	default:
    28  		conn.Close()
    29  	}
    30  }
    31  
    32  // Accept implements net.Listener.
    33  func (l *OutboundListener) Accept() (net.Conn, error) {
    34  	select {
    35  	case <-l.done.Wait():
    36  		return nil, newError("listen closed")
    37  	case c := <-l.buffer:
    38  		return c, nil
    39  	}
    40  }
    41  
    42  // Close implement net.Listener.
    43  func (l *OutboundListener) Close() error {
    44  	common.Must(l.done.Close())
    45  L:
    46  	for {
    47  		select {
    48  		case c := <-l.buffer:
    49  			c.Close()
    50  		default:
    51  			break L
    52  		}
    53  	}
    54  	return nil
    55  }
    56  
    57  // Addr implements net.Listener.
    58  func (l *OutboundListener) Addr() net.Addr {
    59  	return &net.TCPAddr{
    60  		IP:   net.IP{0, 0, 0, 0},
    61  		Port: 0,
    62  	}
    63  }
    64  
    65  // Outbound is a outbound.Handler that handles gRPC connections.
    66  type Outbound struct {
    67  	tag      string
    68  	listener *OutboundListener
    69  	access   sync.RWMutex
    70  	closed   bool
    71  }
    72  
    73  // Dispatch implements outbound.Handler.
    74  func (co *Outbound) Dispatch(ctx context.Context, link *transport.Link) {
    75  	co.access.RLock()
    76  
    77  	if co.closed {
    78  		common.Interrupt(link.Reader)
    79  		common.Interrupt(link.Writer)
    80  		co.access.RUnlock()
    81  		return
    82  	}
    83  
    84  	closeSignal := done.New()
    85  	c := net.NewConnection(net.ConnectionInputMulti(link.Writer), net.ConnectionOutputMulti(link.Reader), net.ConnectionOnClose(closeSignal))
    86  	co.listener.add(c)
    87  	co.access.RUnlock()
    88  	<-closeSignal.Wait()
    89  }
    90  
    91  // Tag implements outbound.Handler.
    92  func (co *Outbound) Tag() string {
    93  	return co.tag
    94  }
    95  
    96  // Start implements common.Runnable.
    97  func (co *Outbound) Start() error {
    98  	co.access.Lock()
    99  	co.closed = false
   100  	co.access.Unlock()
   101  	return nil
   102  }
   103  
   104  // Close implements common.Closable.
   105  func (co *Outbound) Close() error {
   106  	co.access.Lock()
   107  	defer co.access.Unlock()
   108  
   109  	co.closed = true
   110  	return co.listener.Close()
   111  }