Просмотр исходного кода

Support loading config from different formats (#228)

Monsoon 5 лет назад
Родитель
Сommit
1b87264c53
4 измененных файлов с 111 добавлено и 29 удалено
  1. 59 15
      core/config.go
  2. 1 1
      core/functions.go
  3. 44 0
      infra/conf/serial/builder.go
  4. 7 13
      main/run.go

+ 59 - 15
core/config.go

@@ -22,9 +22,13 @@ type ConfigFormat struct {
 // ConfigLoader is a utility to load Xray config from external source.
 // ConfigLoader is a utility to load Xray config from external source.
 type ConfigLoader func(input interface{}) (*Config, error)
 type ConfigLoader func(input interface{}) (*Config, error)
 
 
+// ConfigBuilder is a builder to build core.Config from filenames and formats
+type ConfigBuilder func(files []string, formats []string) (*Config, error)
+
 var (
 var (
-	configLoaderByName = make(map[string]*ConfigFormat)
-	configLoaderByExt  = make(map[string]*ConfigFormat)
+	configLoaderByName    = make(map[string]*ConfigFormat)
+	configLoaderByExt     = make(map[string]*ConfigFormat)
+	ConfigBuilderForFiles ConfigBuilder
 )
 )
 
 
 // RegisterConfigLoader add a new ConfigLoader.
 // RegisterConfigLoader add a new ConfigLoader.
@@ -46,6 +50,21 @@ func RegisterConfigLoader(format *ConfigFormat) error {
 	return nil
 	return nil
 }
 }
 
 
+func GetFormatByExtension(ext string) string {
+	switch strings.ToLower(ext) {
+	case "pb", "protobuf":
+		return "protobuf"
+	case "yaml", "yml":
+		return "yaml"
+	case "toml":
+		return "toml"
+	case "json":
+		return "json"
+	default:
+		return ""
+	}
+}
+
 func getExtension(filename string) string {
 func getExtension(filename string) string {
 	idx := strings.LastIndexByte(filename, '.')
 	idx := strings.LastIndexByte(filename, '.')
 	if idx == -1 {
 	if idx == -1 {
@@ -54,23 +73,48 @@ func getExtension(filename string) string {
 	return filename[idx+1:]
 	return filename[idx+1:]
 }
 }
 
 
-// LoadConfig loads config with given format from given source.
-// input accepts 2 different types:
-// * []string slice of multiple filename/url(s) to open to read
-// * io.Reader that reads a config content (the original way)
-func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) {
-	ext := getExtension(filename)
-	if len(ext) > 0 {
-		if f, found := configLoaderByExt[ext]; found {
-			return f.Loader(input)
+func getFormat(filename string) string {
+	return GetFormatByExtension(getExtension(filename))
+}
+
+func LoadConfig(formatName string, input interface{}) (*Config, error) {
+	switch v := input.(type) {
+	case cmdarg.Arg:
+
+		formats := make([]string, len(v))
+		hasProtobuf := false
+		for i, file := range v {
+			f := getFormat(file)
+			if f == "" {
+				f = formatName
+			}
+			if f == "protobuf" {
+				hasProtobuf = true
+			}
+			formats[i] = f
+		}
+
+		// only one protobuf config file is allowed
+		if hasProtobuf {
+			if len(v) == 1 {
+				return configLoaderByName["protobuf"].Loader(v)
+			} else {
+				return nil, newError("Only one protobuf config file is allowed").AtWarning()
+			}
 		}
 		}
-	}
 
 
-	if f, found := configLoaderByName[formatName]; found {
-		return f.Loader(input)
+		// to avoid import cycle
+		return ConfigBuilderForFiles(v, formats)
+
+	case io.Reader:
+		if f, found := configLoaderByName[formatName]; found {
+			return f.Loader(v)
+		} else {
+			return nil, newError("Unable to load config in", formatName).AtWarning()
+		}
 	}
 	}
 
 
-	return nil, newError("Unable to load config in ", formatName).AtWarning()
+	return nil, newError("Unable to load config").AtWarning()
 }
 }
 
 
 func loadProtobufConfig(data []byte) (*Config, error) {
 func loadProtobufConfig(data []byte) (*Config, error) {

+ 1 - 1
core/functions.go

@@ -25,7 +25,7 @@ func CreateObject(v *Instance, config interface{}) (interface{}, error) {
 //
 //
 // xray:api:stable
 // xray:api:stable
 func StartInstance(configFormat string, configBytes []byte) (*Instance, error) {
 func StartInstance(configFormat string, configBytes []byte) (*Instance, error) {
-	config, err := LoadConfig(configFormat, "", bytes.NewReader(configBytes))
+	config, err := LoadConfig(configFormat, bytes.NewReader(configBytes))
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 44 - 0
infra/conf/serial/builder.go

@@ -0,0 +1,44 @@
+package serial
+
+import (
+	"github.com/xtls/xray-core/core"
+	"github.com/xtls/xray-core/infra/conf"
+	"github.com/xtls/xray-core/main/confloader"
+	"io"
+)
+
+func BuildConfig(files []string, formats []string) (*core.Config, error) {
+
+	cf := &conf.Config{}
+	for i, file := range files {
+		newError("Reading config: ", file).AtInfo().WriteToLog()
+		r, err := confloader.LoadConfig(file)
+		if err != nil {
+			return nil, newError("failed to read config: ", file).Base(err)
+		}
+		c, err := ReaderDecoderByFormat[formats[i]](r)
+		if err != nil {
+			return nil, newError("failed to decode config: ", file).Base(err)
+		}
+		if i == 0 {
+			*cf = *c
+			continue
+		}
+		cf.Override(c, file)
+	}
+	return cf.Build()
+}
+
+type readerDecoder func(io.Reader) (*conf.Config, error)
+
+var (
+	ReaderDecoderByFormat = make(map[string]readerDecoder)
+)
+
+func init() {
+	ReaderDecoderByFormat["json"] = DecodeJSONConfig
+	ReaderDecoderByFormat["yaml"] = DecodeYAMLConfig
+	ReaderDecoderByFormat["toml"] = DecodeTOMLConfig
+
+	core.ConfigBuilderForFiles = BuildConfig
+}

+ 7 - 13
main/run.go

@@ -11,7 +11,6 @@ import (
 	"regexp"
 	"regexp"
 	"runtime"
 	"runtime"
 	"runtime/debug"
 	"runtime/debug"
-	"strings"
 	"syscall"
 	"syscall"
 
 
 	"github.com/xtls/xray-core/common/cmdarg"
 	"github.com/xtls/xray-core/common/cmdarg"
@@ -158,30 +157,25 @@ func getConfigFilePath() cmdarg.Arg {
 }
 }
 
 
 func getConfigFormat() string {
 func getConfigFormat() string {
-	switch strings.ToLower(*format) {
-	case "pb", "protobuf":
-		return "protobuf"
-	case "yaml", "yml":
-		return "yaml"
-	case "toml":
-		return "toml"
-	default:
-		return "json"
+	f := core.GetFormatByExtension(*format)
+	if f == "" {
+		f = "json"
 	}
 	}
+	return f
 }
 }
 
 
 func startXray() (core.Server, error) {
 func startXray() (core.Server, error) {
 	configFiles := getConfigFilePath()
 	configFiles := getConfigFilePath()
 
 
-	config, err := core.LoadConfig(getConfigFormat(), configFiles[0], configFiles)
+	//config, err := core.LoadConfig(getConfigFormat(), configFiles[0], configFiles)
 
 
-	//config, err := core.LoadConfigs(getConfigFormat(), configFiles)
+	c, err := core.LoadConfig(getConfigFormat(), configFiles)
 
 
 	if err != nil {
 	if err != nil {
 		return nil, newError("failed to load config files: [", configFiles.String(), "]").Base(err)
 		return nil, newError("failed to load config files: [", configFiles.String(), "]").Base(err)
 	}
 	}
 
 
-	server, err := core.New(config)
+	server, err := core.New(c)
 	if err != nil {
 	if err != nil {
 		return nil, newError("failed to create server").Base(err)
 		return nil, newError("failed to create server").Base(err)
 	}
 	}