mihomo/adapter/adapter.go

209 lines
4.5 KiB
Go
Raw Normal View History

2021-06-10 14:05:56 +08:00
package adapter
2018-12-22 23:56:42 +08:00
import (
2019-07-02 19:18:03 +08:00
"context"
2018-12-22 23:56:42 +08:00
"encoding/json"
2021-06-10 14:05:56 +08:00
"fmt"
"net"
"net/http"
2022-04-20 01:52:51 +08:00
"net/netip"
2021-06-10 14:05:56 +08:00
"net/url"
"time"
2018-12-22 23:56:42 +08:00
"github.com/Dreamacro/clash/common/atomic"
"github.com/Dreamacro/clash/common/queue"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
2018-12-22 23:56:42 +08:00
)
var UnifiedDelay = atomic.NewBool(false)
type Proxy struct {
C.ProxyAdapter
2022-04-06 01:07:08 +08:00
history *queue.Queue[C.DelayHistory]
alive *atomic.Bool
}
2021-04-29 11:23:14 +08:00
// Alive implements C.Proxy
func (p *Proxy) Alive() bool {
return p.alive.Load()
}
2021-04-29 11:23:14 +08:00
// Dial implements C.Proxy
func (p *Proxy) Dial(metadata *C.Metadata) (C.Conn, error) {
2021-06-10 14:05:56 +08:00
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
defer cancel()
return p.DialContext(ctx, metadata)
}
2021-04-29 11:23:14 +08:00
// DialContext implements C.ProxyAdapter
func (p *Proxy) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
conn, err := p.ProxyAdapter.DialContext(ctx, metadata, opts...)
return conn, err
}
// DialUDP implements C.ProxyAdapter
func (p *Proxy) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout)
defer cancel()
return p.ListenPacketContext(ctx, metadata)
}
// ListenPacketContext implements C.ProxyAdapter
func (p *Proxy) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
pc, err := p.ProxyAdapter.ListenPacketContext(ctx, metadata, opts...)
return pc, err
}
2021-04-29 11:23:14 +08:00
// DelayHistory implements C.Proxy
func (p *Proxy) DelayHistory() []C.DelayHistory {
2022-04-20 01:52:51 +08:00
queueM := p.history.Copy()
histories := []C.DelayHistory{}
2022-04-20 01:52:51 +08:00
for _, item := range queueM {
2022-04-06 01:07:08 +08:00
histories = append(histories, item)
}
return histories
}
2019-09-07 16:23:43 +08:00
// LastDelay return last history record. if proxy is not alive, return the max value of uint16.
2021-04-29 11:23:14 +08:00
// implements C.Proxy
func (p *Proxy) LastDelay() (delay uint16) {
2019-03-23 16:29:27 +08:00
var max uint16 = 0xffff
if !p.alive.Load() {
2019-03-23 16:29:27 +08:00
return max
}
2022-04-06 01:07:08 +08:00
history := p.history.Last()
if history.Delay == 0 {
2019-03-23 16:29:27 +08:00
return max
}
return history.Delay
}
2021-04-29 11:23:14 +08:00
// MarshalJSON implements C.ProxyAdapter
func (p *Proxy) MarshalJSON() ([]byte, error) {
inner, err := p.ProxyAdapter.MarshalJSON()
if err != nil {
return inner, err
}
2022-03-16 12:10:13 +08:00
mapping := map[string]any{}
2022-04-20 01:52:51 +08:00
_ = json.Unmarshal(inner, &mapping)
mapping["history"] = p.DelayHistory()
2019-12-13 00:29:24 +08:00
mapping["name"] = p.Name()
mapping["udp"] = p.SupportUDP()
mapping["xudp"] = p.SupportXUDP()
mapping["tfo"] = p.SupportTFO()
return json.Marshal(mapping)
}
// URLTest get the delay for the specified URL
2021-04-29 11:23:14 +08:00
// implements C.Proxy
2019-07-02 19:18:03 +08:00
func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
defer func() {
p.alive.Store(err == nil)
record := C.DelayHistory{Time: time.Now()}
if err == nil {
record.Delay = t
}
p.history.Put(record)
if p.history.Len() > 10 {
p.history.Pop()
}
}()
unifiedDelay := UnifiedDelay.Load()
addr, err := urlToMetadata(url)
if err != nil {
return
}
start := time.Now()
instance, err := p.DialContext(ctx, &addr)
if err != nil {
return
}
2022-04-20 01:52:51 +08:00
defer func() {
_ = instance.Close()
}()
2019-07-02 19:18:03 +08:00
req, err := http.NewRequest(http.MethodHead, url, nil)
2019-07-02 19:18:03 +08:00
if err != nil {
return
}
req = req.WithContext(ctx)
transport := &http.Transport{
2022-04-20 01:52:51 +08:00
DialContext: func(context.Context, string, string) (net.Conn, error) {
return instance, nil
},
// from http.DefaultTransport
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
2019-07-02 19:18:03 +08:00
client := http.Client{
Timeout: 30 * time.Second,
Transport: transport,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
2022-06-19 17:29:46 +08:00
defer client.CloseIdleConnections()
2022-06-19 17:29:46 +08:00
2019-07-02 19:18:03 +08:00
resp, err := client.Do(req)
2022-06-19 17:29:46 +08:00
if err != nil {
return
}
2022-06-19 17:29:46 +08:00
_ = resp.Body.Close()
if unifiedDelay {
second := time.Now()
resp, err = client.Do(req)
if err == nil {
_ = resp.Body.Close()
start = second
}
}
2022-06-19 17:29:46 +08:00
t = uint16(time.Since(start) / time.Millisecond)
return
}
func NewProxy(adapter C.ProxyAdapter) *Proxy {
2022-04-06 01:07:08 +08:00
return &Proxy{adapter, queue.New[C.DelayHistory](10), atomic.NewBool(true)}
}
2021-06-10 14:05:56 +08:00
func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
u, err := url.Parse(rawURL)
if err != nil {
return
}
port := u.Port()
if port == "" {
switch u.Scheme {
case "https":
port = "443"
case "http":
port = "80"
default:
err = fmt.Errorf("%s scheme not Support", rawURL)
return
}
}
addr = C.Metadata{
2022-11-11 09:19:28 +08:00
Host: u.Hostname(),
DstIP: netip.Addr{},
DstPort: port,
2021-06-10 14:05:56 +08:00
}
return
}