Feature: add experimental provider
This commit is contained in:
207
config/config.go
207
config/config.go
@ -7,8 +7,9 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/component/auth"
|
||||
trie "github.com/Dreamacro/clash/component/domain-trie"
|
||||
"github.com/Dreamacro/clash/component/fakeip"
|
||||
@ -68,6 +69,7 @@ type Config struct {
|
||||
Rules []C.Rule
|
||||
Users []auth.AuthUser
|
||||
Proxies map[string]C.Proxy
|
||||
Providers map[string]provider.ProxyProvider
|
||||
}
|
||||
|
||||
type rawDNS struct {
|
||||
@ -99,12 +101,13 @@ type rawConfig struct {
|
||||
ExternalUI string `yaml:"external-ui"`
|
||||
Secret string `yaml:"secret"`
|
||||
|
||||
Hosts map[string]string `yaml:"hosts"`
|
||||
DNS rawDNS `yaml:"dns"`
|
||||
Experimental Experimental `yaml:"experimental"`
|
||||
Proxy []map[string]interface{} `yaml:"Proxy"`
|
||||
ProxyGroup []map[string]interface{} `yaml:"Proxy Group"`
|
||||
Rule []string `yaml:"Rule"`
|
||||
ProxyProvider map[string]map[string]interface{} `yaml:"proxy-provider"`
|
||||
Hosts map[string]string `yaml:"hosts"`
|
||||
DNS rawDNS `yaml:"dns"`
|
||||
Experimental Experimental `yaml:"experimental"`
|
||||
Proxy []map[string]interface{} `yaml:"Proxy"`
|
||||
ProxyGroup []map[string]interface{} `yaml:"Proxy Group"`
|
||||
Rule []string `yaml:"Rule"`
|
||||
}
|
||||
|
||||
// Parse config
|
||||
@ -146,11 +149,12 @@ func Parse(buf []byte) (*Config, error) {
|
||||
}
|
||||
config.General = general
|
||||
|
||||
proxies, err := parseProxies(rawCfg)
|
||||
proxies, providers, err := parseProxies(rawCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Proxies = proxies
|
||||
config.Providers = providers
|
||||
|
||||
rules, err := parseRules(rawCfg, proxies)
|
||||
if err != nil {
|
||||
@ -171,7 +175,6 @@ func Parse(buf []byte) (*Config, error) {
|
||||
config.Hosts = hosts
|
||||
|
||||
config.Users = parseAuthentication(rawCfg.Authentication)
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@ -210,75 +213,38 @@ func parseGeneral(cfg *rawConfig) (*General, error) {
|
||||
return general, nil
|
||||
}
|
||||
|
||||
func parseProxies(cfg *rawConfig) (map[string]C.Proxy, error) {
|
||||
proxies := make(map[string]C.Proxy)
|
||||
func parseProxies(cfg *rawConfig) (proxies map[string]C.Proxy, providersMap map[string]provider.ProxyProvider, err error) {
|
||||
proxies = make(map[string]C.Proxy)
|
||||
providersMap = make(map[string]provider.ProxyProvider)
|
||||
proxyList := []string{}
|
||||
proxiesConfig := cfg.Proxy
|
||||
groupsConfig := cfg.ProxyGroup
|
||||
providersConfig := cfg.ProxyProvider
|
||||
|
||||
decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true})
|
||||
defer func() {
|
||||
// Destroy already created provider when err != nil
|
||||
if err != nil {
|
||||
for _, provider := range providersMap {
|
||||
provider.Destroy()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
proxies["DIRECT"] = adapters.NewProxy(adapters.NewDirect())
|
||||
proxies["REJECT"] = adapters.NewProxy(adapters.NewReject())
|
||||
proxies["DIRECT"] = outbound.NewProxy(outbound.NewDirect())
|
||||
proxies["REJECT"] = outbound.NewProxy(outbound.NewReject())
|
||||
proxyList = append(proxyList, "DIRECT", "REJECT")
|
||||
|
||||
// parse proxy
|
||||
for idx, mapping := range proxiesConfig {
|
||||
proxyType, existType := mapping["type"].(string)
|
||||
if !existType {
|
||||
return nil, fmt.Errorf("Proxy %d missing type", idx)
|
||||
}
|
||||
|
||||
var proxy C.ProxyAdapter
|
||||
err := fmt.Errorf("cannot parse")
|
||||
switch proxyType {
|
||||
case "ss":
|
||||
ssOption := &adapters.ShadowSocksOption{}
|
||||
err = decoder.Decode(mapping, ssOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = adapters.NewShadowSocks(*ssOption)
|
||||
case "socks5":
|
||||
socksOption := &adapters.Socks5Option{}
|
||||
err = decoder.Decode(mapping, socksOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy = adapters.NewSocks5(*socksOption)
|
||||
case "http":
|
||||
httpOption := &adapters.HttpOption{}
|
||||
err = decoder.Decode(mapping, httpOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy = adapters.NewHttp(*httpOption)
|
||||
case "vmess":
|
||||
vmessOption := &adapters.VmessOption{}
|
||||
err = decoder.Decode(mapping, vmessOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = adapters.NewVmess(*vmessOption)
|
||||
case "snell":
|
||||
snellOption := &adapters.SnellOption{}
|
||||
err = decoder.Decode(mapping, snellOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = adapters.NewSnell(*snellOption)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupport proxy type: %s", proxyType)
|
||||
}
|
||||
|
||||
proxy, err := outbound.ParseProxy(mapping)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Proxy [%d]: %s", idx, err.Error())
|
||||
return nil, nil, fmt.Errorf("Proxy %d: %w", idx, err)
|
||||
}
|
||||
|
||||
if _, exist := proxies[proxy.Name()]; exist {
|
||||
return nil, fmt.Errorf("Proxy %s is the duplicate name", proxy.Name())
|
||||
return nil, nil, fmt.Errorf("Proxy %s is the duplicate name", proxy.Name())
|
||||
}
|
||||
proxies[proxy.Name()] = adapters.NewProxy(proxy)
|
||||
proxies[proxy.Name()] = proxy
|
||||
proxyList = append(proxyList, proxy.Name())
|
||||
}
|
||||
|
||||
@ -286,95 +252,62 @@ func parseProxies(cfg *rawConfig) (map[string]C.Proxy, error) {
|
||||
for idx, mapping := range groupsConfig {
|
||||
groupName, existName := mapping["name"].(string)
|
||||
if !existName {
|
||||
return nil, fmt.Errorf("ProxyGroup %d: missing name", idx)
|
||||
return nil, nil, fmt.Errorf("ProxyGroup %d: missing name", idx)
|
||||
}
|
||||
proxyList = append(proxyList, groupName)
|
||||
}
|
||||
|
||||
// check if any loop exists and sort the ProxyGroups
|
||||
if err := proxyGroupsDagSort(groupsConfig, decoder); err != nil {
|
||||
return nil, err
|
||||
if err := proxyGroupsDagSort(groupsConfig); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// parse and initial providers
|
||||
for name, mapping := range providersConfig {
|
||||
if name == provider.ReservedName {
|
||||
return nil, nil, fmt.Errorf("can not defined a provider called `%s`", provider.ReservedName)
|
||||
}
|
||||
|
||||
pd, err := provider.ParseProxyProvider(name, mapping)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
providersMap[name] = pd
|
||||
}
|
||||
|
||||
for _, provider := range providersMap {
|
||||
log.Infoln("Start initial provider %s", provider.Name())
|
||||
if err := provider.Initial(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// parse proxy group
|
||||
for _, mapping := range groupsConfig {
|
||||
groupType, existType := mapping["type"].(string)
|
||||
groupName, _ := mapping["name"].(string)
|
||||
if !existType {
|
||||
return nil, fmt.Errorf("ProxyGroup %s: missing type", groupName)
|
||||
}
|
||||
|
||||
if _, exist := proxies[groupName]; exist {
|
||||
return nil, fmt.Errorf("ProxyGroup %s: the duplicate name", groupName)
|
||||
}
|
||||
var group C.ProxyAdapter
|
||||
ps := []C.Proxy{}
|
||||
|
||||
err := fmt.Errorf("cannot parse")
|
||||
switch groupType {
|
||||
case "url-test":
|
||||
urlTestOption := &adapters.URLTestOption{}
|
||||
err = decoder.Decode(mapping, urlTestOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
ps, err = getProxies(proxies, urlTestOption.Proxies)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
}
|
||||
group, err = adapters.NewURLTest(*urlTestOption, ps)
|
||||
case "select":
|
||||
selectorOption := &adapters.SelectorOption{}
|
||||
err = decoder.Decode(mapping, selectorOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
ps, err = getProxies(proxies, selectorOption.Proxies)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
}
|
||||
group, err = adapters.NewSelector(selectorOption.Name, ps)
|
||||
case "fallback":
|
||||
fallbackOption := &adapters.FallbackOption{}
|
||||
err = decoder.Decode(mapping, fallbackOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
ps, err = getProxies(proxies, fallbackOption.Proxies)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
}
|
||||
group, err = adapters.NewFallback(*fallbackOption, ps)
|
||||
case "load-balance":
|
||||
loadBalanceOption := &adapters.LoadBalanceOption{}
|
||||
err = decoder.Decode(mapping, loadBalanceOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
ps, err = getProxies(proxies, loadBalanceOption.Proxies)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
}
|
||||
group, err = adapters.NewLoadBalance(*loadBalanceOption, ps)
|
||||
}
|
||||
for idx, mapping := range groupsConfig {
|
||||
group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Proxy %s: %s", groupName, err.Error())
|
||||
return nil, nil, fmt.Errorf("ProxyGroup[%d]: %w", idx, err)
|
||||
}
|
||||
proxies[groupName] = adapters.NewProxy(group)
|
||||
|
||||
groupName := group.Name()
|
||||
if _, exist := proxies[groupName]; exist {
|
||||
return nil, nil, fmt.Errorf("ProxyGroup %s: the duplicate name", groupName)
|
||||
}
|
||||
|
||||
proxies[groupName] = outbound.NewProxy(group)
|
||||
}
|
||||
|
||||
ps := []C.Proxy{}
|
||||
for _, v := range proxyList {
|
||||
ps = append(ps, proxies[v])
|
||||
}
|
||||
pd, _ := provider.NewCompatibleProvier(provider.ReservedName, ps, nil)
|
||||
providersMap[provider.ReservedName] = pd
|
||||
|
||||
global, _ := adapters.NewSelector("GLOBAL", ps)
|
||||
proxies["GLOBAL"] = adapters.NewProxy(global)
|
||||
return proxies, nil
|
||||
global := outboundgroup.NewSelector("GLOBAL", []provider.ProxyProvider{pd})
|
||||
proxies["GLOBAL"] = outbound.NewProxy(global)
|
||||
return proxies, providersMap, nil
|
||||
}
|
||||
|
||||
func parseRules(cfg *rawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) {
|
||||
|
@ -4,9 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
func trimArr(arr []string) (r []string) {
|
||||
@ -16,18 +15,6 @@ func trimArr(arr []string) (r []string) {
|
||||
return
|
||||
}
|
||||
|
||||
func getProxies(mapping map[string]C.Proxy, list []string) ([]C.Proxy, error) {
|
||||
var ps []C.Proxy
|
||||
for _, name := range list {
|
||||
p, ok := mapping[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("'%s' not found", name)
|
||||
}
|
||||
ps = append(ps, p)
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func or(pointers ...*int) *int {
|
||||
for _, p := range pointers {
|
||||
if p != nil {
|
||||
@ -40,8 +27,7 @@ func or(pointers ...*int) *int {
|
||||
// Check if ProxyGroups form DAG(Directed Acyclic Graph), and sort all ProxyGroups by dependency order.
|
||||
// Meanwhile, record the original index in the config file.
|
||||
// If loop is detected, return an error with location of loop.
|
||||
func proxyGroupsDagSort(groupsConfig []map[string]interface{}, decoder *structure.Decoder) error {
|
||||
|
||||
func proxyGroupsDagSort(groupsConfig []map[string]interface{}) error {
|
||||
type graphNode struct {
|
||||
indegree int
|
||||
// topological order
|
||||
@ -50,34 +36,36 @@ func proxyGroupsDagSort(groupsConfig []map[string]interface{}, decoder *structur
|
||||
data map[string]interface{}
|
||||
// `outdegree` and `from` are used in loop locating
|
||||
outdegree int
|
||||
option *outboundgroup.GroupCommonOption
|
||||
from []string
|
||||
}
|
||||
|
||||
decoder := structure.NewDecoder(structure.Option{TagName: "group", WeaklyTypedInput: true})
|
||||
graph := make(map[string]*graphNode)
|
||||
|
||||
// Step 1.1 build dependency graph
|
||||
for _, mapping := range groupsConfig {
|
||||
option := &adapters.ProxyGroupOption{}
|
||||
err := decoder.Decode(mapping, option)
|
||||
groupName := option.Name
|
||||
if err != nil {
|
||||
return fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
|
||||
option := &outboundgroup.GroupCommonOption{}
|
||||
if err := decoder.Decode(mapping, option); err != nil {
|
||||
return fmt.Errorf("ProxyGroup %s: %s", option.Name, err.Error())
|
||||
}
|
||||
|
||||
groupName := option.Name
|
||||
if node, ok := graph[groupName]; ok {
|
||||
if node.data != nil {
|
||||
return fmt.Errorf("ProxyGroup %s: duplicate group name", groupName)
|
||||
}
|
||||
node.data = mapping
|
||||
node.option = option
|
||||
} else {
|
||||
graph[groupName] = &graphNode{0, -1, mapping, 0, nil}
|
||||
graph[groupName] = &graphNode{0, -1, mapping, 0, option, nil}
|
||||
}
|
||||
|
||||
for _, proxy := range option.Proxies {
|
||||
if node, ex := graph[proxy]; ex {
|
||||
node.indegree++
|
||||
} else {
|
||||
graph[proxy] = &graphNode{1, -1, nil, 0, nil}
|
||||
graph[proxy] = &graphNode{1, -1, nil, 0, nil, nil}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,14 +83,19 @@ func proxyGroupsDagSort(groupsConfig []map[string]interface{}, decoder *structur
|
||||
for ; len(queue) > 0; queue = queue[1:] {
|
||||
name := queue[0]
|
||||
node := graph[name]
|
||||
if node.data != nil {
|
||||
if node.option != nil {
|
||||
index++
|
||||
groupsConfig[len(groupsConfig)-index] = node.data
|
||||
for _, proxy := range node.data["proxies"].([]interface{}) {
|
||||
child := graph[proxy.(string)]
|
||||
if len(node.option.Proxies) == 0 {
|
||||
delete(graph, name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, proxy := range node.option.Proxies {
|
||||
child := graph[proxy]
|
||||
child.indegree--
|
||||
if child.indegree == 0 {
|
||||
queue = append(queue, proxy.(string))
|
||||
queue = append(queue, proxy)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,12 +110,17 @@ func proxyGroupsDagSort(groupsConfig []map[string]interface{}, decoder *structur
|
||||
// if loop is detected, locate the loop and throw an error
|
||||
// Step 2.1 rebuild the graph, fill `outdegree` and `from` filed
|
||||
for name, node := range graph {
|
||||
if node.data == nil {
|
||||
if node.option == nil {
|
||||
continue
|
||||
}
|
||||
for _, proxy := range node.data["proxies"].([]interface{}) {
|
||||
|
||||
if len(node.option.Proxies) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, proxy := range node.option.Proxies {
|
||||
node.outdegree++
|
||||
child := graph[proxy.(string)]
|
||||
child := graph[proxy]
|
||||
if child.from == nil {
|
||||
child.from = make([]string, 0, child.indegree)
|
||||
}
|
||||
|
Reference in New Issue
Block a user