config.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package core
  2. import (
  3. "io"
  4. "strings"
  5. "github.com/xtls/xray-core/common"
  6. "github.com/xtls/xray-core/common/buf"
  7. "github.com/xtls/xray-core/common/cmdarg"
  8. "github.com/xtls/xray-core/common/errors"
  9. "github.com/xtls/xray-core/main/confloader"
  10. "google.golang.org/protobuf/proto"
  11. )
  12. // ConfigFormat is a configurable format of Xray config file.
  13. type ConfigFormat struct {
  14. Name string
  15. Extension []string
  16. Loader ConfigLoader
  17. }
  18. // ConfigLoader is a utility to load Xray config from external source.
  19. type ConfigLoader func(input interface{}) (*Config, error)
  20. // ConfigBuilder is a builder to build core.Config from filenames and formats
  21. type ConfigBuilder func(files []string, formats []string) (*Config, error)
  22. // ConfigsMerger merge multiple json configs into on config
  23. type ConfigsMerger func(files []string, formats []string) (string, error)
  24. var (
  25. configLoaderByName = make(map[string]*ConfigFormat)
  26. configLoaderByExt = make(map[string]*ConfigFormat)
  27. ConfigBuilderForFiles ConfigBuilder
  28. ConfigMergedFormFiles ConfigsMerger
  29. )
  30. // RegisterConfigLoader add a new ConfigLoader.
  31. func RegisterConfigLoader(format *ConfigFormat) error {
  32. name := strings.ToLower(format.Name)
  33. if _, found := configLoaderByName[name]; found {
  34. return errors.New(format.Name, " already registered.")
  35. }
  36. configLoaderByName[name] = format
  37. for _, ext := range format.Extension {
  38. lext := strings.ToLower(ext)
  39. if f, found := configLoaderByExt[lext]; found {
  40. return errors.New(ext, " already registered to ", f.Name)
  41. }
  42. configLoaderByExt[lext] = format
  43. }
  44. return nil
  45. }
  46. func GetMergedConfig(args cmdarg.Arg) (string, error) {
  47. files := make([]string, 0)
  48. formats := make([]string, 0)
  49. supported := []string{"json", "yaml", "toml"}
  50. for _, file := range args {
  51. format := getFormat(file)
  52. for _, s := range supported {
  53. if s == format {
  54. files = append(files, file)
  55. formats = append(formats, format)
  56. break
  57. }
  58. }
  59. }
  60. return ConfigMergedFormFiles(files, formats)
  61. }
  62. func GetFormatByExtension(ext string) string {
  63. switch strings.ToLower(ext) {
  64. case "pb", "protobuf":
  65. return "protobuf"
  66. case "yaml", "yml":
  67. return "yaml"
  68. case "toml":
  69. return "toml"
  70. case "json", "jsonc":
  71. return "json"
  72. default:
  73. return ""
  74. }
  75. }
  76. func getExtension(filename string) string {
  77. idx := strings.LastIndexByte(filename, '.')
  78. if idx == -1 {
  79. return ""
  80. }
  81. return filename[idx+1:]
  82. }
  83. func getFormat(filename string) string {
  84. return GetFormatByExtension(getExtension(filename))
  85. }
  86. func LoadConfig(formatName string, input interface{}) (*Config, error) {
  87. switch v := input.(type) {
  88. case cmdarg.Arg:
  89. formats := make([]string, len(v))
  90. hasProtobuf := false
  91. for i, file := range v {
  92. var f string
  93. if formatName == "auto" {
  94. if file != "stdin:" {
  95. f = getFormat(file)
  96. } else {
  97. f = "json"
  98. }
  99. } else {
  100. f = formatName
  101. }
  102. if f == "" {
  103. return nil, errors.New("Failed to get format of ", file).AtWarning()
  104. }
  105. if f == "protobuf" {
  106. hasProtobuf = true
  107. }
  108. formats[i] = f
  109. }
  110. // only one protobuf config file is allowed
  111. if hasProtobuf {
  112. if len(v) == 1 {
  113. return configLoaderByName["protobuf"].Loader(v)
  114. } else {
  115. return nil, errors.New("Only one protobuf config file is allowed").AtWarning()
  116. }
  117. }
  118. // to avoid import cycle
  119. return ConfigBuilderForFiles(v, formats)
  120. case io.Reader:
  121. if f, found := configLoaderByName[formatName]; found {
  122. return f.Loader(v)
  123. } else {
  124. return nil, errors.New("Unable to load config in", formatName).AtWarning()
  125. }
  126. }
  127. return nil, errors.New("Unable to load config").AtWarning()
  128. }
  129. func loadProtobufConfig(data []byte) (*Config, error) {
  130. config := new(Config)
  131. if err := proto.Unmarshal(data, config); err != nil {
  132. return nil, err
  133. }
  134. return config, nil
  135. }
  136. func init() {
  137. common.Must(RegisterConfigLoader(&ConfigFormat{
  138. Name: "Protobuf",
  139. Extension: []string{"pb"},
  140. Loader: func(input interface{}) (*Config, error) {
  141. switch v := input.(type) {
  142. case cmdarg.Arg:
  143. r, err := confloader.LoadConfig(v[0])
  144. common.Must(err)
  145. data, err := buf.ReadAllToBytes(r)
  146. common.Must(err)
  147. return loadProtobufConfig(data)
  148. case io.Reader:
  149. data, err := buf.ReadAllToBytes(v)
  150. common.Must(err)
  151. return loadProtobufConfig(data)
  152. default:
  153. return nil, errors.New("unknow type")
  154. }
  155. },
  156. }))
  157. }