clash/adapters/outbound/base.go

196 lines
3.6 KiB
Go
Raw Normal View History

2018-12-22 15:56:42 +00:00
package adapters
import (
2019-07-02 11:18:03 +00:00
"context"
2018-12-22 15:56:42 +00:00
"encoding/json"
2019-04-23 15:29:36 +00:00
"errors"
"net"
"net/http"
"time"
2018-12-22 15:56:42 +00:00
"github.com/Dreamacro/clash/common/queue"
2018-12-22 15:56:42 +00:00
C "github.com/Dreamacro/clash/constant"
)
var (
defaultURLTestTimeout = time.Second * 5
)
2018-12-22 15:56:42 +00:00
type Base struct {
name string
tp C.AdapterType
2019-04-23 15:29:36 +00:00
udp bool
2018-12-22 15:56:42 +00:00
}
func (b *Base) Name() string {
return b.name
}
func (b *Base) Type() C.AdapterType {
return b.tp
}
func (b *Base) DialUDP(metadata *C.Metadata) (C.PacketConn, net.Addr, error) {
2019-04-23 15:29:36 +00:00
return nil, nil, errors.New("no support")
}
func (b *Base) SupportUDP() bool {
return b.udp
}
func (b *Base) Destroy() {}
2018-12-22 15:56:42 +00:00
func (b *Base) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]string{
"type": b.Type().String(),
})
}
type conn struct {
net.Conn
chain C.Chain
}
func (c *conn) Chains() C.Chain {
return c.chain
}
func (c *conn) AppendToChains(a C.ProxyAdapter) {
c.chain = append(c.chain, a.Name())
}
func newConn(c net.Conn, a C.ProxyAdapter) C.Conn {
return &conn{c, []string{a.Name()}}
}
type packetConn struct {
net.PacketConn
chain C.Chain
}
func (c *packetConn) Chains() C.Chain {
return c.chain
}
func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
c.chain = append(c.chain, a.Name())
}
func newPacketConn(c net.PacketConn, a C.ProxyAdapter) C.PacketConn {
return &packetConn{c, []string{a.Name()}}
}
type Proxy struct {
C.ProxyAdapter
history *queue.Queue
alive bool
}
func (p *Proxy) Alive() bool {
return p.alive
}
func (p *Proxy) Dial(metadata *C.Metadata) (C.Conn, error) {
conn, err := p.ProxyAdapter.Dial(metadata)
if err != nil {
p.alive = false
}
return conn, err
}
func (p *Proxy) DelayHistory() []C.DelayHistory {
queue := p.history.Copy()
histories := []C.DelayHistory{}
for _, item := range queue {
histories = append(histories, item.(C.DelayHistory))
}
return histories
}
2019-03-23 08:29:27 +00:00
// LastDelay return last history record. if proxy is not alive, return the max value of int16.
func (p *Proxy) LastDelay() (delay uint16) {
2019-03-23 08:29:27 +00:00
var max uint16 = 0xffff
if !p.alive {
return max
}
head := p.history.First()
if head == nil {
2019-03-23 08:29:27 +00:00
return max
}
history := head.(C.DelayHistory)
if history.Delay == 0 {
2019-03-23 08:29:27 +00:00
return max
}
return history.Delay
}
func (p *Proxy) MarshalJSON() ([]byte, error) {
inner, err := p.ProxyAdapter.MarshalJSON()
if err != nil {
return inner, err
}
mapping := map[string]interface{}{}
json.Unmarshal(inner, &mapping)
mapping["history"] = p.DelayHistory()
return json.Marshal(mapping)
}
// URLTest get the delay for the specified URL
2019-07-02 11:18:03 +00:00
func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
defer func() {
p.alive = 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()
}
}()
addr, err := urlToMetadata(url)
if err != nil {
return
}
start := time.Now()
2019-03-23 08:29:27 +00:00
instance, err := p.Dial(&addr)
if err != nil {
return
}
defer instance.Close()
2019-07-02 11:18:03 +00:00
req, err := http.NewRequest(http.MethodHead, url, nil)
2019-07-02 11:18:03 +00:00
if err != nil {
return
}
req = req.WithContext(ctx)
transport := &http.Transport{
Dial: func(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 11:18:03 +00:00
client := http.Client{Transport: transport}
2019-07-02 11:18:03 +00:00
resp, err := client.Do(req)
if err != nil {
return
}
resp.Body.Close()
t = uint16(time.Since(start) / time.Millisecond)
return
}
func NewProxy(adapter C.ProxyAdapter) *Proxy {
return &Proxy{adapter, queue.New(10), true}
}