From cb118d43719ccde61db1eb3dd64e99fefcb02548 Mon Sep 17 00:00:00 2001 From: Dreamacro <305009791@qq.com> Date: Sat, 22 Dec 2018 23:56:42 +0800 Subject: [PATCH] Chore: improve outbound architecture --- adapters/outbound/base.go | 26 ++++++++++++++++++ adapters/outbound/direct.go | 45 ++++++++------------------------ adapters/outbound/fallback.go | 22 +++++++--------- adapters/outbound/http.go | 40 +++++----------------------- adapters/outbound/reject.go | 40 +++++++--------------------- adapters/outbound/selector.go | 18 +++++-------- adapters/outbound/shadowsocks.go | 35 ++++++------------------- adapters/outbound/socks5.go | 40 +++++----------------------- adapters/outbound/urltest.go | 18 +++++-------- adapters/outbound/util.go | 2 +- adapters/outbound/vmess.go | 40 +++++----------------------- constant/adapters.go | 7 +---- tunnel/connection.go | 11 ++++---- 13 files changed, 105 insertions(+), 239 deletions(-) create mode 100644 adapters/outbound/base.go diff --git a/adapters/outbound/base.go b/adapters/outbound/base.go new file mode 100644 index 00000000..9701d0e1 --- /dev/null +++ b/adapters/outbound/base.go @@ -0,0 +1,26 @@ +package adapters + +import ( + "encoding/json" + + C "github.com/Dreamacro/clash/constant" +) + +type Base struct { + name string + tp C.AdapterType +} + +func (b *Base) Name() string { + return b.name +} + +func (b *Base) Type() C.AdapterType { + return b.tp +} + +func (b *Base) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]string{ + "type": b.Type().String(), + }) +} diff --git a/adapters/outbound/direct.go b/adapters/outbound/direct.go index 052d3f3b..b46bf500 100644 --- a/adapters/outbound/direct.go +++ b/adapters/outbound/direct.go @@ -1,38 +1,16 @@ package adapters import ( - "encoding/json" "net" C "github.com/Dreamacro/clash/constant" ) -// DirectAdapter is a directly connected adapter -type DirectAdapter struct { - conn net.Conn +type Direct struct { + *Base } -// Close is used to close connection -func (d *DirectAdapter) Close() { - d.conn.Close() -} - -// Conn is used to http request -func (d *DirectAdapter) Conn() net.Conn { - return d.conn -} - -type Direct struct{} - -func (d *Direct) Name() string { - return "DIRECT" -} - -func (d *Direct) Type() C.AdapterType { - return C.Direct -} - -func (d *Direct) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { +func (d *Direct) Generator(metadata *C.Metadata) (net.Conn, error) { address := net.JoinHostPort(metadata.Host, metadata.Port) if metadata.IP != nil { address = net.JoinHostPort(metadata.IP.String(), metadata.Port) @@ -40,18 +18,17 @@ func (d *Direct) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err er c, err := net.DialTimeout("tcp", address, tcpTimeout) if err != nil { - return + return nil, err } tcpKeepAlive(c) - return &DirectAdapter{conn: c}, nil -} - -func (d *Direct) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]string{ - "type": d.Type().String(), - }) + return c, nil } func NewDirect() *Direct { - return &Direct{} + return &Direct{ + Base: &Base{ + name: "DIRECT", + tp: C.Direct, + }, + } } diff --git a/adapters/outbound/fallback.go b/adapters/outbound/fallback.go index 0acaabbb..ea456fd3 100644 --- a/adapters/outbound/fallback.go +++ b/adapters/outbound/fallback.go @@ -3,6 +3,7 @@ package adapters import ( "encoding/json" "errors" + "net" "sync" "time" @@ -15,7 +16,7 @@ type proxy struct { } type Fallback struct { - name string + *Base proxies []*proxy rawURL string interval time.Duration @@ -29,14 +30,6 @@ type FallbackOption struct { Interval int `proxy:"interval"` } -func (f *Fallback) Name() string { - return f.name -} - -func (f *Fallback) Type() C.AdapterType { - return C.Fallback -} - func (f *Fallback) Now() string { _, proxy := f.findNextValidProxy(0) if proxy != nil { @@ -45,7 +38,7 @@ func (f *Fallback) Now() string { return f.proxies[0].RawProxy.Name() } -func (f *Fallback) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { +func (f *Fallback) Generator(metadata *C.Metadata) (net.Conn, error) { idx := 0 var proxy *proxy for { @@ -53,13 +46,13 @@ func (f *Fallback) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err if proxy == nil { break } - adapter, err = proxy.RawProxy.Generator(metadata) + adapter, err := proxy.RawProxy.Generator(metadata) if err != nil { proxy.Valid = false idx++ continue } - return + return adapter, err } return f.proxies[0].RawProxy.Generator(metadata) } @@ -138,7 +131,10 @@ func NewFallback(option FallbackOption, proxies []C.Proxy) (*Fallback, error) { } Fallback := &Fallback{ - name: option.Name, + Base: &Base{ + name: option.Name, + tp: C.Fallback, + }, proxies: warpperProxies, rawURL: option.URL, interval: interval, diff --git a/adapters/outbound/http.go b/adapters/outbound/http.go index 8938aa84..2892d102 100644 --- a/adapters/outbound/http.go +++ b/adapters/outbound/http.go @@ -5,7 +5,6 @@ import ( "bytes" "crypto/tls" "encoding/base64" - "encoding/json" "errors" "fmt" "io" @@ -16,23 +15,9 @@ import ( C "github.com/Dreamacro/clash/constant" ) -// HTTPAdapter is a proxy adapter -type HTTPAdapter struct { - conn net.Conn -} - -// Close is used to close connection -func (ha *HTTPAdapter) Close() { - ha.conn.Close() -} - -func (ha *HTTPAdapter) Conn() net.Conn { - return ha.conn -} - type Http struct { + *Base addr string - name string user string pass string tls bool @@ -50,15 +35,7 @@ type HttpOption struct { SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` } -func (h *Http) Name() string { - return h.name -} - -func (h *Http) Type() C.AdapterType { - return C.Http -} - -func (h *Http) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { +func (h *Http) Generator(metadata *C.Metadata) (net.Conn, error) { c, err := net.DialTimeout("tcp", h.addr, tcpTimeout) if err == nil && h.tls { cc := tls.Client(c, h.tlsConfig) @@ -74,7 +51,7 @@ func (h *Http) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err erro return nil, err } - return &HTTPAdapter{conn: c}, nil + return c, nil } func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { @@ -118,12 +95,6 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { return fmt.Errorf("can not connect remote err code: %d", resp.StatusCode) } -func (h *Http) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]string{ - "type": h.Type().String(), - }) -} - func NewHttp(option HttpOption) *Http { var tlsConfig *tls.Config if option.TLS { @@ -137,8 +108,11 @@ func NewHttp(option HttpOption) *Http { } return &Http{ + Base: &Base{ + name: option.Name, + tp: C.Http, + }, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), - name: option.Name, user: option.UserName, pass: option.Password, tls: option.TLS, diff --git a/adapters/outbound/reject.go b/adapters/outbound/reject.go index 5a5357c0..9f49ae85 100644 --- a/adapters/outbound/reject.go +++ b/adapters/outbound/reject.go @@ -1,7 +1,6 @@ package adapters import ( - "encoding/json" "io" "net" "time" @@ -9,42 +8,21 @@ import ( C "github.com/Dreamacro/clash/constant" ) -// RejectAdapter is a reject connected adapter -type RejectAdapter struct { - conn net.Conn -} - -// Close is used to close connection -func (r *RejectAdapter) Close() {} - -// Conn is used to http request -func (r *RejectAdapter) Conn() net.Conn { - return r.conn -} - type Reject struct { + *Base } -func (r *Reject) Name() string { - return "REJECT" -} - -func (r *Reject) Type() C.AdapterType { - return C.Reject -} - -func (r *Reject) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { - return &RejectAdapter{conn: &NopConn{}}, nil -} - -func (r *Reject) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]string{ - "type": r.Type().String(), - }) +func (r *Reject) Generator(metadata *C.Metadata) (net.Conn, error) { + return &NopConn{}, nil } func NewReject() *Reject { - return &Reject{} + return &Reject{ + Base: &Base{ + name: "REJECT", + tp: C.Reject, + }, + } } type NopConn struct{} diff --git a/adapters/outbound/selector.go b/adapters/outbound/selector.go index e4dbd6d9..28f39c40 100644 --- a/adapters/outbound/selector.go +++ b/adapters/outbound/selector.go @@ -3,13 +3,14 @@ package adapters import ( "encoding/json" "errors" + "net" "sort" C "github.com/Dreamacro/clash/constant" ) type Selector struct { - name string + *Base selected C.Proxy proxies map[string]C.Proxy } @@ -19,15 +20,7 @@ type SelectorOption struct { Proxies []string `proxy:"proxies"` } -func (s *Selector) Name() string { - return s.name -} - -func (s *Selector) Type() C.AdapterType { - return C.Selector -} - -func (s *Selector) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { +func (s *Selector) Generator(metadata *C.Metadata) (net.Conn, error) { return s.selected.Generator(metadata) } @@ -68,7 +61,10 @@ func NewSelector(name string, proxies []C.Proxy) (*Selector, error) { } s := &Selector{ - name: name, + Base: &Base{ + name: name, + tp: C.Selector, + }, proxies: mapping, selected: proxies[0], } diff --git a/adapters/outbound/shadowsocks.go b/adapters/outbound/shadowsocks.go index 31356084..feb8f098 100644 --- a/adapters/outbound/shadowsocks.go +++ b/adapters/outbound/shadowsocks.go @@ -7,30 +7,16 @@ import ( "net" "strconv" - "github.com/Dreamacro/clash/component/simple-obfs" + obfs "github.com/Dreamacro/clash/component/simple-obfs" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/go-shadowsocks2/core" "github.com/Dreamacro/go-shadowsocks2/socks" ) -// ShadowsocksAdapter is a shadowsocks adapter -type ShadowsocksAdapter struct { - conn net.Conn -} - -// Close is used to close connection -func (ss *ShadowsocksAdapter) Close() { - ss.conn.Close() -} - -func (ss *ShadowsocksAdapter) Conn() net.Conn { - return ss.conn -} - type ShadowSocks struct { + *Base server string - name string obfs string obfsHost string cipher core.Cipher @@ -46,15 +32,7 @@ type ShadowSocksOption struct { ObfsHost string `proxy:"obfs-host,omitempty"` } -func (ss *ShadowSocks) Name() string { - return ss.name -} - -func (ss *ShadowSocks) Type() C.AdapterType { - return C.Shadowsocks -} - -func (ss *ShadowSocks) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { +func (ss *ShadowSocks) Generator(metadata *C.Metadata) (net.Conn, error) { c, err := net.DialTimeout("tcp", ss.server, tcpTimeout) if err != nil { return nil, fmt.Errorf("%s connect error", ss.server) @@ -69,7 +47,7 @@ func (ss *ShadowSocks) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, } c = ss.cipher.StreamConn(c) _, err = c.Write(serializesSocksAddr(metadata)) - return &ShadowsocksAdapter{conn: c}, err + return c, err } func (ss *ShadowSocks) MarshalJSON() ([]byte, error) { @@ -94,8 +72,11 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { } return &ShadowSocks{ + Base: &Base{ + name: option.Name, + tp: C.Shadowsocks, + }, server: server, - name: option.Name, cipher: ciph, obfs: obfs, obfsHost: obfsHost, diff --git a/adapters/outbound/socks5.go b/adapters/outbound/socks5.go index dc958235..2b4613d6 100644 --- a/adapters/outbound/socks5.go +++ b/adapters/outbound/socks5.go @@ -3,7 +3,6 @@ package adapters import ( "bytes" "crypto/tls" - "encoding/json" "errors" "fmt" "io" @@ -15,23 +14,9 @@ import ( "github.com/Dreamacro/go-shadowsocks2/socks" ) -// Socks5Adapter is a shadowsocks adapter -type Socks5Adapter struct { - conn net.Conn -} - -// Close is used to close connection -func (ss *Socks5Adapter) Close() { - ss.conn.Close() -} - -func (ss *Socks5Adapter) Conn() net.Conn { - return ss.conn -} - type Socks5 struct { + *Base addr string - name string user string pass string tls bool @@ -49,15 +34,7 @@ type Socks5Option struct { SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` } -func (ss *Socks5) Name() string { - return ss.name -} - -func (ss *Socks5) Type() C.AdapterType { - return C.Socks5 -} - -func (ss *Socks5) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { +func (ss *Socks5) Generator(metadata *C.Metadata) (net.Conn, error) { c, err := net.DialTimeout("tcp", ss.addr, tcpTimeout) if err == nil && ss.tls { @@ -73,13 +50,7 @@ func (ss *Socks5) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err e if err := ss.shakeHand(metadata, c); err != nil { return nil, err } - return &Socks5Adapter{conn: c}, nil -} - -func (ss *Socks5) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]string{ - "type": ss.Type().String(), - }) + return c, nil } func (ss *Socks5) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { @@ -154,8 +125,11 @@ func NewSocks5(option Socks5Option) *Socks5 { } return &Socks5{ + Base: &Base{ + name: option.Name, + tp: C.Socks5, + }, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), - name: option.Name, user: option.UserName, pass: option.Password, tls: option.TLS, diff --git a/adapters/outbound/urltest.go b/adapters/outbound/urltest.go index 1466c277..b8b144aa 100644 --- a/adapters/outbound/urltest.go +++ b/adapters/outbound/urltest.go @@ -3,6 +3,7 @@ package adapters import ( "encoding/json" "errors" + "net" "sort" "sync" "sync/atomic" @@ -12,7 +13,7 @@ import ( ) type URLTest struct { - name string + *Base proxies []C.Proxy rawURL string fast C.Proxy @@ -28,19 +29,11 @@ type URLTestOption struct { Interval int `proxy:"interval"` } -func (u *URLTest) Name() string { - return u.name -} - -func (u *URLTest) Type() C.AdapterType { - return C.URLTest -} - func (u *URLTest) Now() string { return u.fast.Name() } -func (u *URLTest) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { +func (u *URLTest) Generator(metadata *C.Metadata) (net.Conn, error) { a, err := u.fast.Generator(metadata) if err != nil { go u.speedTest() @@ -128,7 +121,10 @@ func NewURLTest(option URLTestOption, proxies []C.Proxy) (*URLTest, error) { interval := time.Duration(option.Interval) * time.Second urlTest := &URLTest{ - name: option.Name, + Base: &Base{ + name: option.Name, + tp: C.URLTest, + }, proxies: proxies[:], rawURL: option.URL, fast: proxies[0], diff --git a/adapters/outbound/util.go b/adapters/outbound/util.go index f38b55e6..6dedc9b7 100644 --- a/adapters/outbound/util.go +++ b/adapters/outbound/util.go @@ -36,7 +36,7 @@ func DelayTest(proxy C.Proxy, url string) (t int16, err error) { defer instance.Close() transport := &http.Transport{ Dial: func(string, string) (net.Conn, error) { - return instance.Conn(), nil + return instance, nil }, // from http.DefaultTransport MaxIdleConns: 100, diff --git a/adapters/outbound/vmess.go b/adapters/outbound/vmess.go index dc3eb2d2..67820acc 100644 --- a/adapters/outbound/vmess.go +++ b/adapters/outbound/vmess.go @@ -1,7 +1,6 @@ package adapters import ( - "encoding/json" "fmt" "net" "strconv" @@ -11,22 +10,8 @@ import ( C "github.com/Dreamacro/clash/constant" ) -// VmessAdapter is a vmess adapter -type VmessAdapter struct { - conn net.Conn -} - -// Close is used to close connection -func (v *VmessAdapter) Close() { - v.conn.Close() -} - -func (v *VmessAdapter) Conn() net.Conn { - return v.conn -} - type Vmess struct { - name string + *Base server string client *vmess.Client } @@ -45,28 +30,14 @@ type VmessOption struct { SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` } -func (v *Vmess) Name() string { - return v.name -} - -func (v *Vmess) Type() C.AdapterType { - return C.Vmess -} - -func (v *Vmess) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err error) { +func (v *Vmess) Generator(metadata *C.Metadata) (net.Conn, error) { c, err := net.DialTimeout("tcp", v.server, tcpTimeout) if err != nil { return nil, fmt.Errorf("%s connect error", v.server) } tcpKeepAlive(c) c, err = v.client.New(c, parseVmessAddr(metadata)) - return &VmessAdapter{conn: c}, err -} - -func (v *Vmess) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]interface{}{ - "type": v.Type().String(), - }) + return c, err } func NewVmess(option VmessOption) (*Vmess, error) { @@ -89,7 +60,10 @@ func NewVmess(option VmessOption) (*Vmess, error) { } return &Vmess{ - name: option.Name, + Base: &Base{ + name: option.Name, + tp: C.Vmess, + }, server: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), client: client, }, nil diff --git a/constant/adapters.go b/constant/adapters.go index 6746fb5a..293b50ed 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -17,11 +17,6 @@ const ( Vmess ) -type ProxyAdapter interface { - Conn() net.Conn - Close() -} - type ServerAdapter interface { Metadata() *Metadata Close() @@ -30,7 +25,7 @@ type ServerAdapter interface { type Proxy interface { Name() string Type() AdapterType - Generator(metadata *Metadata) (ProxyAdapter, error) + Generator(metadata *Metadata) (net.Conn, error) MarshalJSON() ([]byte, error) } diff --git a/tunnel/connection.go b/tunnel/connection.go index b2271d57..8b9832d7 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -9,8 +9,7 @@ import ( "sync" "time" - "github.com/Dreamacro/clash/adapters/inbound" - C "github.com/Dreamacro/clash/constant" + adapters "github.com/Dreamacro/clash/adapters/inbound" ) const ( @@ -22,8 +21,8 @@ const ( var bufPool = sync.Pool{New: func() interface{} { return make([]byte, bufferSize) }} -func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, proxy C.ProxyAdapter) { - conn := newTrafficTrack(proxy.Conn(), t.traffic) +func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) { + conn := newTrafficTrack(outbound, t.traffic) req := request.R host := req.Host keepalive := true @@ -76,8 +75,8 @@ func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, proxy C.ProxyAdapter) } } -func (t *Tunnel) handleSOCKS(request *adapters.SocketAdapter, proxy C.ProxyAdapter) { - conn := newTrafficTrack(proxy.Conn(), t.traffic) +func (t *Tunnel) handleSOCKS(request *adapters.SocketAdapter, outbound net.Conn) { + conn := newTrafficTrack(outbound, t.traffic) relay(request.Conn(), conn) }