Chore: improve outbound architecture

This commit is contained in:
Dreamacro 2018-12-22 23:56:42 +08:00
parent a7cfc81885
commit cb118d4371
13 changed files with 105 additions and 239 deletions

26
adapters/outbound/base.go Normal file
View File

@ -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(),
})
}

View File

@ -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,
},
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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{}

View File

@ -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],
}

View File

@ -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,

View File

@ -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,

View File

@ -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],

View File

@ -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,

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}