github.com/xraypb/xray-core@v1.6.6/app/commander/outbound.go (about)

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