Merge branch 'dev' into Alpha
This commit is contained in:
commit
10383e2701
9 changed files with 119 additions and 71 deletions
|
@ -151,25 +151,32 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
return http.ErrUseLastResponse
|
return http.ErrUseLastResponse
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
defer client.CloseIdleConnections()
|
defer client.CloseIdleConnections()
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
|
||||||
if unifiedDelay {
|
if unifiedDelay {
|
||||||
start = time.Now()
|
second := time.Now()
|
||||||
resp, err = client.Do(req)
|
resp, err = client.Do(req)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return
|
_ = resp.Body.Close()
|
||||||
|
start = second
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = resp.Body.Close()
|
|
||||||
t = uint16(time.Since(start) / time.Millisecond)
|
t = uint16(time.Since(start) / time.Millisecond)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,24 +41,32 @@ var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`)
|
||||||
type Hysteria struct {
|
type Hysteria struct {
|
||||||
*Base
|
*Base
|
||||||
|
|
||||||
client *core.Client
|
client *core.Client
|
||||||
clientTransport *transport.ClientTransport
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
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) {
|
hdc := hyDialerWithContext{
|
||||||
return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewConn(tcpConn, h), nil
|
return NewConn(tcpConn, h), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
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) {
|
hdc := hyDialerWithContext{
|
||||||
return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -93,7 +101,7 @@ func (c *HysteriaOption) Speed() (uint64, uint64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
down = stringToBps(c.Down)
|
down = stringToBps(c.Down)
|
||||||
if up == 0 {
|
if down == 0 {
|
||||||
return 0, 0, fmt.Errorf("invaild download speed: %s", c.Down)
|
return 0, 0, fmt.Errorf("invaild download speed: %s", c.Down)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,8 +199,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
},
|
},
|
||||||
client: client,
|
client: client,
|
||||||
clientTransport: clientTransport,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,8 +262,15 @@ func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type hyDialer func() (net.PacketConn, error)
|
type hyDialerWithContext struct {
|
||||||
|
hyDialer func() (net.PacketConn, error)
|
||||||
func (h hyDialer) ListenPacket() (net.PacketConn, error) {
|
ctx context.Context
|
||||||
return h()
|
}
|
||||||
|
|
||||||
|
func (h *hyDialerWithContext) ListenPacket() (net.PacketConn, error) {
|
||||||
|
return h.hyDialer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hyDialerWithContext) Context() context.Context {
|
||||||
|
return h.ctx
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,11 +111,11 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string) (map[string]uint16
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
delay, err := proxy.URLTest(ctx, url)
|
delay, err := proxy.URLTest(ctx, url)
|
||||||
lock.Lock()
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
lock.Lock()
|
||||||
mp[proxy.Name()] = delay
|
mp[proxy.Name()] = delay
|
||||||
|
lock.Unlock()
|
||||||
}
|
}
|
||||||
lock.Unlock()
|
|
||||||
|
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -202,6 +202,7 @@ func newFetcher[V any](name string, interval time.Duration, vehicle types.Vehicl
|
||||||
parser: parser,
|
parser: parser,
|
||||||
done: make(chan struct{}, 1),
|
done: make(chan struct{}, 1),
|
||||||
onUpdate: onUpdate,
|
onUpdate: onUpdate,
|
||||||
|
interval: interval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/Dreamacro/clash/common/singledo"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/batch"
|
"github.com/Dreamacro/clash/common/batch"
|
||||||
|
@ -26,6 +27,7 @@ type HealthCheck struct {
|
||||||
lazy bool
|
lazy bool
|
||||||
lastTouch *atomic.Int64
|
lastTouch *atomic.Int64
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
singleDo *singledo.Single[struct{}]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *HealthCheck) process() {
|
func (hc *HealthCheck) process() {
|
||||||
|
@ -63,17 +65,21 @@ func (hc *HealthCheck) touch() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *HealthCheck) check() {
|
func (hc *HealthCheck) check() {
|
||||||
b, _ := batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](10))
|
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
||||||
for _, proxy := range hc.proxies {
|
b, _ := batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](10))
|
||||||
p := proxy
|
for _, proxy := range hc.proxies {
|
||||||
b.Go(p.Name(), func() (bool, error) {
|
p := proxy
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
|
b.Go(p.Name(), func() (bool, error) {
|
||||||
defer cancel()
|
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
|
||||||
_, _ = p.URLTest(ctx, hc.url)
|
defer cancel()
|
||||||
return false, nil
|
_, _ = p.URLTest(ctx, hc.url)
|
||||||
})
|
return false, nil
|
||||||
}
|
})
|
||||||
b.Wait()
|
}
|
||||||
|
|
||||||
|
b.Wait()
|
||||||
|
return struct{}{}, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *HealthCheck) close() {
|
func (hc *HealthCheck) close() {
|
||||||
|
@ -88,5 +94,6 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *He
|
||||||
lazy: lazy,
|
lazy: lazy,
|
||||||
lastTouch: atomic.NewInt64(0),
|
lastTouch: atomic.NewInt64(0),
|
||||||
done: make(chan struct{}, 1),
|
done: make(chan struct{}, 1),
|
||||||
|
singleDo: singledo.NewSingle[struct{}](time.Second),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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+"4", host, opt.direct, false)
|
||||||
go startRacer(ctx, network+"6", host, opt.direct, true)
|
go startRacer(ctx, network+"6", host, opt.direct, true)
|
||||||
|
|
||||||
for res := range results {
|
count := 2
|
||||||
if res.error == nil {
|
for i := 0; i < count; i++ {
|
||||||
return res.Conn, nil
|
select {
|
||||||
}
|
case res := <-results:
|
||||||
|
if res.error == nil {
|
||||||
if !res.ipv6 {
|
return res.Conn, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
results := make(chan dialResult)
|
||||||
|
|
||||||
tcpRacer := func(ctx context.Context, ip netip.Addr) {
|
tcpRacer := func(ctx context.Context, ip netip.Addr) {
|
||||||
result := dialResult{ip: ip}
|
result := dialResult{ip: ip}
|
||||||
|
|
||||||
|
@ -252,13 +256,13 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
connCount := len(ips)
|
connCount := len(ips)
|
||||||
for res := range results {
|
for i := 0; i < connCount; i++ {
|
||||||
connCount--
|
select {
|
||||||
if res.error == nil {
|
case res := <-results:
|
||||||
return res.Conn, nil
|
if res.error == nil {
|
||||||
}
|
return res.Conn, nil
|
||||||
|
}
|
||||||
if connCount == 0 {
|
case <-ctx.Done():
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ func (dc *quicClient) getSession(ctx context.Context) (quic.Connection, error) {
|
||||||
|
|
||||||
func (dc *quicClient) openSession(ctx context.Context) (quic.Connection, error) {
|
func (dc *quicClient) openSession(ctx context.Context) (quic.Connection, error) {
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: false,
|
||||||
NextProtos: []string{
|
NextProtos: []string{
|
||||||
NextProtoDQ,
|
NextProtoDQ,
|
||||||
},
|
},
|
||||||
|
|
33
dns/util.go
33
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) {
|
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
|
networkType := C.TCP
|
||||||
if network == "udp" {
|
if network == "udp" {
|
||||||
if !adapter.SupportUDP() {
|
|
||||||
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", adapterName)
|
|
||||||
}
|
|
||||||
networkType = C.UDP
|
networkType = C.UDP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +171,29 @@ func dialContextExtra(ctx context.Context, adapterName string, network string, d
|
||||||
DstPort: port,
|
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 {
|
if networkType == C.UDP {
|
||||||
packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...)
|
packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -64,7 +64,7 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
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()
|
defer cancel()
|
||||||
|
|
||||||
dm, err := group.URLTest(ctx, url)
|
dm, err := group.URLTest(ctx, url)
|
||||||
|
|
Loading…
Reference in a new issue