github.com/kelleygo/clashcore@v1.0.2/transport/tuic/pool_client.go (about)

     1  package tuic
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net"
     7  	"runtime"
     8  	"sync"
     9  	"time"
    10  
    11  	N "github.com/kelleygo/clashcore/common/net"
    12  	C "github.com/kelleygo/clashcore/constant"
    13  	"github.com/kelleygo/clashcore/log"
    14  
    15  	"github.com/metacubex/quic-go"
    16  
    17  	list "github.com/bahlo/generic-list-go"
    18  )
    19  
    20  type dialResult struct {
    21  	transport *quic.Transport
    22  	addr      net.Addr
    23  	err       error
    24  }
    25  
    26  type PoolClient struct {
    27  	newClientOptionV4 *ClientOptionV4
    28  	newClientOptionV5 *ClientOptionV5
    29  	dialResultMap     map[C.Dialer]dialResult
    30  	dialResultMutex   *sync.Mutex
    31  	tcpClients        *list.List[Client]
    32  	tcpClientsMutex   *sync.Mutex
    33  	udpClients        *list.List[Client]
    34  	udpClientsMutex   *sync.Mutex
    35  }
    36  
    37  func (t *PoolClient) DialContextWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.Conn, error) {
    38  	newDialFn := func(ctx context.Context, dialer C.Dialer) (transport *quic.Transport, addr net.Addr, err error) {
    39  		return t.dial(ctx, dialer, dialFn)
    40  	}
    41  	conn, err := t.getClient(false, dialer).DialContextWithDialer(ctx, metadata, dialer, newDialFn)
    42  	if errors.Is(err, TooManyOpenStreams) {
    43  		conn, err = t.newClient(false, dialer).DialContextWithDialer(ctx, metadata, dialer, newDialFn)
    44  	}
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return N.NewRefConn(conn, t), err
    49  }
    50  
    51  func (t *PoolClient) ListenPacketWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.PacketConn, error) {
    52  	newDialFn := func(ctx context.Context, dialer C.Dialer) (transport *quic.Transport, addr net.Addr, err error) {
    53  		return t.dial(ctx, dialer, dialFn)
    54  	}
    55  	pc, err := t.getClient(true, dialer).ListenPacketWithDialer(ctx, metadata, dialer, newDialFn)
    56  	if errors.Is(err, TooManyOpenStreams) {
    57  		pc, err = t.newClient(true, dialer).ListenPacketWithDialer(ctx, metadata, dialer, newDialFn)
    58  	}
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return N.NewRefPacketConn(pc, t), nil
    63  }
    64  
    65  func (t *PoolClient) dial(ctx context.Context, dialer C.Dialer, dialFn DialFunc) (transport *quic.Transport, addr net.Addr, err error) {
    66  	t.dialResultMutex.Lock()
    67  	dr, ok := t.dialResultMap[dialer]
    68  	t.dialResultMutex.Unlock()
    69  	if ok {
    70  		return dr.transport, dr.addr, dr.err
    71  	}
    72  
    73  	transport, addr, err = dialFn(ctx, dialer)
    74  	if err != nil {
    75  		return nil, nil, err
    76  	}
    77  
    78  	if _, ok := transport.Conn.(*net.UDPConn); ok { // only cache the system's UDPConn
    79  		transport.SetSingleUse(false) // don't close transport in each dial
    80  		dr.transport, dr.addr, dr.err = transport, addr, err
    81  
    82  		t.dialResultMutex.Lock()
    83  		t.dialResultMap[dialer] = dr
    84  		t.dialResultMutex.Unlock()
    85  	}
    86  
    87  	return transport, addr, err
    88  }
    89  
    90  func (t *PoolClient) forceClose() {
    91  	t.dialResultMutex.Lock()
    92  	defer t.dialResultMutex.Unlock()
    93  	for key := range t.dialResultMap {
    94  		transport := t.dialResultMap[key].transport
    95  		if transport != nil {
    96  			_ = transport.Close()
    97  		}
    98  		delete(t.dialResultMap, key)
    99  	}
   100  }
   101  
   102  func (t *PoolClient) newClient(udp bool, dialer C.Dialer) (client Client) {
   103  	clients := t.tcpClients
   104  	clientsMutex := t.tcpClientsMutex
   105  	if udp {
   106  		clients = t.udpClients
   107  		clientsMutex = t.udpClientsMutex
   108  	}
   109  
   110  	clientsMutex.Lock()
   111  	defer clientsMutex.Unlock()
   112  
   113  	if t.newClientOptionV4 != nil {
   114  		client = NewClientV4(t.newClientOptionV4, udp, dialer)
   115  	} else {
   116  		client = NewClientV5(t.newClientOptionV5, udp, dialer)
   117  	}
   118  
   119  	client.SetLastVisited(time.Now())
   120  
   121  	clients.PushFront(client)
   122  	return client
   123  }
   124  
   125  func (t *PoolClient) getClient(udp bool, dialer C.Dialer) Client {
   126  	clients := t.tcpClients
   127  	clientsMutex := t.tcpClientsMutex
   128  	if udp {
   129  		clients = t.udpClients
   130  		clientsMutex = t.udpClientsMutex
   131  	}
   132  	var bestClient Client
   133  
   134  	func() {
   135  		clientsMutex.Lock()
   136  		defer clientsMutex.Unlock()
   137  		for it := clients.Front(); it != nil; {
   138  			client := it.Value
   139  			if client == nil {
   140  				next := it.Next()
   141  				clients.Remove(it)
   142  				it = next
   143  				continue
   144  			}
   145  			if client.DialerRef() == dialer {
   146  				if bestClient == nil {
   147  					bestClient = client
   148  				} else {
   149  					if client.OpenStreams() < bestClient.OpenStreams() {
   150  						bestClient = client
   151  					}
   152  				}
   153  			}
   154  			it = it.Next()
   155  		}
   156  	}()
   157  	for it := clients.Front(); it != nil; {
   158  		client := it.Value
   159  		if client != bestClient && client.OpenStreams() == 0 && time.Now().Sub(client.LastVisited()) > 30*time.Minute {
   160  			client.Close()
   161  			next := it.Next()
   162  			clients.Remove(it)
   163  			it = next
   164  			continue
   165  		}
   166  		it = it.Next()
   167  	}
   168  
   169  	if bestClient == nil {
   170  		return t.newClient(udp, dialer)
   171  	} else {
   172  		bestClient.SetLastVisited(time.Now())
   173  		return bestClient
   174  	}
   175  }
   176  
   177  func NewPoolClientV4(clientOption *ClientOptionV4) *PoolClient {
   178  	p := &PoolClient{
   179  		dialResultMap:   make(map[C.Dialer]dialResult),
   180  		dialResultMutex: &sync.Mutex{},
   181  		tcpClients:      list.New[Client](),
   182  		tcpClientsMutex: &sync.Mutex{},
   183  		udpClients:      list.New[Client](),
   184  		udpClientsMutex: &sync.Mutex{},
   185  	}
   186  	newClientOption := *clientOption
   187  	p.newClientOptionV4 = &newClientOption
   188  	runtime.SetFinalizer(p, closeClientPool)
   189  	log.Debugln("New TuicV4 PoolClient at %p", p)
   190  	return p
   191  }
   192  
   193  func NewPoolClientV5(clientOption *ClientOptionV5) *PoolClient {
   194  	p := &PoolClient{
   195  		dialResultMap:   make(map[C.Dialer]dialResult),
   196  		dialResultMutex: &sync.Mutex{},
   197  		tcpClients:      list.New[Client](),
   198  		tcpClientsMutex: &sync.Mutex{},
   199  		udpClients:      list.New[Client](),
   200  		udpClientsMutex: &sync.Mutex{},
   201  	}
   202  	newClientOption := *clientOption
   203  	p.newClientOptionV5 = &newClientOption
   204  	runtime.SetFinalizer(p, closeClientPool)
   205  	log.Debugln("New TuicV5 PoolClient at %p", p)
   206  	return p
   207  }
   208  
   209  func closeClientPool(client *PoolClient) {
   210  	log.Debugln("Close Tuic PoolClient at %p", client)
   211  	client.forceClose()
   212  }