github.com/metacubex/mihomo@v1.18.5/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/metacubex/mihomo/common/net" 12 C "github.com/metacubex/mihomo/constant" 13 "github.com/metacubex/mihomo/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 }