From 109a76e1fc0ae147bd635647ad12a30c39a6d96f Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 19 Jun 2022 17:29:46 +0800 Subject: [PATCH 01/10] fix: url test http response not closed --- adapter/adapter.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index b1867998..e99cc0dd 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -156,20 +156,27 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) { return http.ErrUseLastResponse }, } + defer client.CloseIdleConnections() + resp, err := client.Do(req) + if err != nil { return } + _ = resp.Body.Close() + if unifiedDelay { start = time.Now() - resp, err = client.Do(req) + respRepeat, err := client.Do(req) if err != nil { - return + return 0, err } + + _ = respRepeat.Body.Close() } - _ = resp.Body.Close() + t = uint16(time.Since(start) / time.Millisecond) return } From 4ba34ce6729d921bfa2f851f9a7580235bd3b5ae Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 25 Jun 2022 08:53:04 +0800 Subject: [PATCH 02/10] chore: healthcheck only once check at same time --- adapter/outboundgroup/groupbase.go | 4 ++-- adapter/provider/healthcheck.go | 29 ++++++++++++++++++----------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index 42b61dd4..5ec0c999 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -111,11 +111,11 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string) (map[string]uint16 wg.Add(1) go func() { delay, err := proxy.URLTest(ctx, url) - lock.Lock() if err == nil { + lock.Lock() mp[proxy.Name()] = delay + lock.Unlock() } - lock.Unlock() wg.Done() }() diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index cc664c9e..8e736f6c 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/Dreamacro/clash/common/singledo" "time" "github.com/Dreamacro/clash/common/batch" @@ -26,6 +27,7 @@ type HealthCheck struct { lazy bool lastTouch *atomic.Int64 done chan struct{} + singleDo *singledo.Single[struct{}] } func (hc *HealthCheck) process() { @@ -63,17 +65,21 @@ func (hc *HealthCheck) touch() { } func (hc *HealthCheck) check() { - b, _ := batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](10)) - for _, proxy := range hc.proxies { - p := proxy - b.Go(p.Name(), func() (bool, error) { - ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout) - defer cancel() - _, _ = p.URLTest(ctx, hc.url) - return false, nil - }) - } - b.Wait() + _, _, _ = hc.singleDo.Do(func() (struct{}, error) { + b, _ := batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](10)) + for _, proxy := range hc.proxies { + p := proxy + b.Go(p.Name(), func() (bool, error) { + ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout) + defer cancel() + _, _ = p.URLTest(ctx, hc.url) + return false, nil + }) + } + + b.Wait() + return struct{}{}, nil + }) } func (hc *HealthCheck) close() { @@ -88,5 +94,6 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *He lazy: lazy, lastTouch: atomic.NewInt64(0), done: make(chan struct{}, 1), + singleDo: singledo.NewSingle[struct{}](time.Second), } } From 2cdf4a0532572959455180440c79b909647b10ef Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 25 Jun 2022 08:24:34 +0800 Subject: [PATCH 03/10] chore: RESTful test group use request context --- hub/route/groups.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hub/route/groups.go b/hub/route/groups.go index e877bfd0..13133e9c 100644 --- a/hub/route/groups.go +++ b/hub/route/groups.go @@ -64,7 +64,7 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) { return } - ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout)) + ctx, cancel := context.WithTimeout(r.Context(), time.Millisecond*time.Duration(timeout)) defer cancel() dm, err := group.URLTest(ctx, url) From 8c079bf5bc7e3b0c30e3f250d1b1372a2b2818d4 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 25 Jun 2022 09:16:28 +0800 Subject: [PATCH 04/10] fix: tcp concurrent force close when context done --- adapter/adapter.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index e99cc0dd..1be877c7 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -151,6 +151,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) { } client := http.Client{ + Timeout: 30 * time.Second, Transport: transport, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse @@ -168,13 +169,12 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) { _ = resp.Body.Close() if unifiedDelay { - start = time.Now() - respRepeat, err := client.Do(req) - if err != nil { - return 0, err + second := time.Now() + resp, err = client.Do(req) + if err == nil { + _ = resp.Body.Close() + start = second } - - _ = respRepeat.Body.Close() } t = uint16(time.Since(start) / time.Millisecond) From 9c70e649ca8d4851df1010db49dd34b66bfbb1b3 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 20 Jun 2022 22:30:04 +0800 Subject: [PATCH 05/10] fix: disable doq skip verify cert --- dns/doq.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/doq.go b/dns/doq.go index af4c3833..aafea97f 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -129,7 +129,7 @@ func (dc *quicClient) getSession(ctx context.Context) (quic.Connection, error) { func (dc *quicClient) openSession(ctx context.Context) (quic.Connection, error) { tlsConfig := &tls.Config{ - InsecureSkipVerify: true, + InsecureSkipVerify: false, NextProtos: []string{ NextProtoDQ, }, From 0d55b288051afc44d984c06b2c20a92944193b5a Mon Sep 17 00:00:00 2001 From: Skyxim Date: Tue, 21 Jun 2022 22:59:35 +0800 Subject: [PATCH 06/10] chore: dns interface name --- dns/util.go | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/dns/util.go b/dns/util.go index 4ea511a9..7abbbe97 100644 --- a/dns/util.go +++ b/dns/util.go @@ -152,17 +152,9 @@ func (wpc *wrapPacketConn) LocalAddr() net.Addr { } func dialContextExtra(ctx context.Context, adapterName string, network string, dstIP netip.Addr, port string, opts ...dialer.Option) (net.Conn, error) { - adapter, ok := tunnel.Proxies()[adapterName] - if !ok { - opts = append(opts, dialer.WithInterface(adapterName)) - adapter, _ = tunnel.Proxies()[tunnel.Direct.String()] - } - networkType := C.TCP if network == "udp" { - if !adapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", adapterName) - } + networkType = C.UDP } @@ -179,6 +171,29 @@ func dialContextExtra(ctx context.Context, adapterName string, network string, d DstPort: port, } + adapter, ok := tunnel.Proxies()[adapterName] + if !ok { + opts = append(opts, dialer.WithInterface(adapterName)) + if C.TCP == networkType { + return dialer.DialContext(ctx, network, dstIP.String()+":"+port, opts...) + } else { + packetConn, err := dialer.ListenPacket(ctx, network, dstIP.String()+":"+port, opts...) + if err != nil { + return nil, err + } + + return &wrapPacketConn{ + PacketConn: packetConn, + rAddr: metadata.UDPAddr(), + }, nil + + } + } + + if networkType == C.UDP && !adapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", adapterName) + } + if networkType == C.UDP { packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { From f979491013c58068385531c507778ccb90abc5b6 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 25 Jun 2022 09:00:35 +0800 Subject: [PATCH 07/10] fix: tcp concurrent force close when context done --- component/dialer/dialer.go | 60 ++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index e0401d42..bfba0079 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -4,11 +4,10 @@ import ( "context" "errors" "fmt" + "github.com/Dreamacro/clash/component/resolver" "net" "net/netip" "sync" - - "github.com/Dreamacro/clash/component/resolver" ) var ( @@ -171,25 +170,31 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt go startRacer(ctx, network+"4", host, opt.direct, false) go startRacer(ctx, network+"6", host, opt.direct, true) - for res := range results { - if res.error == nil { - return res.Conn, nil - } - - if !res.ipv6 { - primary = res - } else { - fallback = res - } - - if primary.done && fallback.done { - if primary.resolved { - return nil, primary.error - } else if fallback.resolved { - return nil, fallback.error - } else { - return nil, primary.error + count := 2 + for i := 0; i < count; i++ { + select { + case res := <-results: + if res.error == nil { + return res.Conn, nil } + + if !res.ipv6 { + primary = res + } else { + fallback = res + } + + if primary.done && fallback.done { + if primary.resolved { + return nil, primary.error + } else if fallback.resolved { + return nil, fallback.error + } else { + return nil, primary.error + } + } + case <-ctx.Done(): + break } } @@ -225,7 +230,6 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr } results := make(chan dialResult) - tcpRacer := func(ctx context.Context, ip netip.Addr) { result := dialResult{ip: ip} @@ -252,13 +256,13 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr } connCount := len(ips) - for res := range results { - connCount-- - if res.error == nil { - return res.Conn, nil - } - - if connCount == 0 { + for i := 0; i < connCount; i++ { + select { + case res := <-results: + if res.error == nil { + return res.Conn, nil + } + case <-ctx.Done(): break } } From 669961e496bb2cce0972b550beb654270480540f Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 25 Jun 2022 12:42:52 +0800 Subject: [PATCH 08/10] fix: proxy provider force update on init --- adapter/provider/fetcher.go | 1 + 1 file changed, 1 insertion(+) diff --git a/adapter/provider/fetcher.go b/adapter/provider/fetcher.go index 47b364e6..07fb1237 100644 --- a/adapter/provider/fetcher.go +++ b/adapter/provider/fetcher.go @@ -202,6 +202,7 @@ func newFetcher[V any](name string, interval time.Duration, vehicle types.Vehicl parser: parser, done: make(chan struct{}, 1), onUpdate: onUpdate, + interval: interval, } } From 2ba933d16a50e508e8260c7e1b0c40ef5e822c4d Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 25 Jun 2022 12:43:47 +0800 Subject: [PATCH 09/10] chore: hysteria params verify --- adapter/outbound/hysteria.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index a3eca0eb..60b2efa0 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -93,7 +93,7 @@ func (c *HysteriaOption) Speed() (uint64, uint64, error) { } down = stringToBps(c.Down) - if up == 0 { + if down == 0 { return 0, 0, fmt.Errorf("invaild download speed: %s", c.Down) } From f4b9f2965ffc1e2a9d72b52ef1a400adfbd92cc9 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 26 Jun 2022 21:52:22 +0800 Subject: [PATCH 10/10] fix: hysteria dial use external context --- adapter/outbound/hysteria.go | 42 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 60b2efa0..7b4bcd78 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -41,24 +41,32 @@ var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`) type Hysteria struct { *Base - client *core.Client - clientTransport *transport.ClientTransport + client *core.Client } func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), hyDialer(func() (net.PacketConn, error) { - return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) - })) + hdc := hyDialerWithContext{ + ctx: ctx, + hyDialer: func() (net.PacketConn, error) { + return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) + }, + } + tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), &hdc) if err != nil { return nil, err } + return NewConn(tcpConn, h), nil } func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - udpConn, err := h.client.DialUDP(hyDialer(func() (net.PacketConn, error) { - return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) - })) + hdc := hyDialerWithContext{ + ctx: ctx, + hyDialer: func() (net.PacketConn, error) { + return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) + }, + } + udpConn, err := h.client.DialUDP(&hdc) if err != nil { return nil, err } @@ -191,8 +199,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { iface: option.Interface, rmark: option.RoutingMark, }, - client: client, - clientTransport: clientTransport, + client: client, }, nil } @@ -255,8 +262,15 @@ func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { return } -type hyDialer func() (net.PacketConn, error) - -func (h hyDialer) ListenPacket() (net.PacketConn, error) { - return h() +type hyDialerWithContext struct { + hyDialer func() (net.PacketConn, error) + ctx context.Context +} + +func (h *hyDialerWithContext) ListenPacket() (net.PacketConn, error) { + return h.hyDialer() +} + +func (h *hyDialerWithContext) Context() context.Context { + return h.ctx }