config.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package core
  2. import (
  3. "fmt"
  4. "io"
  5. "log"
  6. "os"
  7. "path/filepath"
  8. "reflect"
  9. "strings"
  10. "google.golang.org/protobuf/proto"
  11. "github.com/v2fly/v2ray-core/v5/common"
  12. "github.com/v2fly/v2ray-core/v5/common/buf"
  13. "github.com/v2fly/v2ray-core/v5/common/cmdarg"
  14. "github.com/v2fly/v2ray-core/v5/common/pq"
  15. )
  16. const (
  17. // FormatAuto represents all available formats by auto selecting
  18. FormatAuto = "auto"
  19. // FormatJSON represents json format
  20. FormatJSON = "json"
  21. // FormatTOML represents toml format
  22. FormatTOML = "toml"
  23. // FormatYAML represents yaml format
  24. FormatYAML = "yaml"
  25. // FormatProtobuf represents protobuf format
  26. FormatProtobuf = "protobuf"
  27. // FormatProtobufShort is the short of FormatProtobuf
  28. FormatProtobufShort = "pb"
  29. )
  30. // ConfigFormat is a configurable format of V2Ray config file.
  31. type ConfigFormat struct {
  32. Name []string
  33. Extension []string
  34. Priority int
  35. Loader ConfigLoader
  36. }
  37. // ConfigLoader is a utility to load V2Ray config from external source.
  38. type ConfigLoader func(input interface{}) (*Config, error)
  39. var (
  40. configLoaders = pq.NewMaxPriorityQueue[*ConfigFormat, int]()
  41. configLoaderByName = make(map[string]*ConfigFormat)
  42. configLoaderByExt = make(map[string]*ConfigFormat)
  43. )
  44. // RegisterConfigLoader add a new ConfigLoader.
  45. func RegisterConfigLoader(format *ConfigFormat) error {
  46. for _, name := range format.Name {
  47. if _, found := configLoaderByName[name]; found {
  48. return newError(name, " already registered.")
  49. }
  50. configLoaderByName[name] = format
  51. }
  52. for _, ext := range format.Extension {
  53. lext := strings.ToLower(ext)
  54. if f, found := configLoaderByExt[lext]; found {
  55. return newError(ext, " already registered to ", f.Name)
  56. }
  57. configLoaderByExt[lext] = format
  58. }
  59. configLoaders.Push(format, format.Priority)
  60. return nil
  61. }
  62. func getExtension(filename string) string {
  63. ext := filepath.Ext(filename)
  64. return strings.ToLower(ext)
  65. }
  66. // GetLoaderExtensions get config loader extensions.
  67. func GetLoaderExtensions(formatName string) ([]string, error) {
  68. if formatName == FormatAuto {
  69. return GetAllExtensions(), nil
  70. }
  71. if f, found := configLoaderByName[formatName]; found {
  72. return f.Extension, nil
  73. }
  74. return nil, newError("config loader not found: ", formatName).AtWarning()
  75. }
  76. // GetAllExtensions get all extensions supported
  77. func GetAllExtensions() []string {
  78. extensions := make([]string, 0)
  79. for _, f := range configLoaderByName {
  80. extensions = append(extensions, f.Extension...)
  81. }
  82. return extensions
  83. }
  84. // LoadConfig loads multiple config with given format from given source.
  85. // input accepts:
  86. // * string of a single filename/url(s) to open to read
  87. // * []string slice of multiple filename/url(s) to open to read
  88. // * io.Reader that reads a config content (the original way)
  89. func LoadConfig(formatName string, input interface{}) (*Config, error) {
  90. cnt := getInputCount(input)
  91. if cnt == 0 {
  92. log.Println("Using config from STDIN")
  93. input = os.Stdin
  94. cnt = 1
  95. }
  96. if formatName == FormatAuto && cnt == 1 {
  97. // This ensures only to call auto loader for multiple files,
  98. // so that it can only care about merging scenarios
  99. return loadSingleConfigAutoFormat(input)
  100. }
  101. // if input is a slice with single element, extract it
  102. // so that unmergeable loaders don't need to deal with
  103. // slices
  104. s := reflect.Indirect(reflect.ValueOf(input))
  105. k := s.Kind()
  106. if (k == reflect.Slice || k == reflect.Array) && s.Len() == 1 {
  107. value := reflect.Indirect(s.Index(0))
  108. if value.Kind() == reflect.String {
  109. // string type alias
  110. input = fmt.Sprint(value.Interface())
  111. } else {
  112. input = value.Interface()
  113. }
  114. }
  115. f, found := configLoaderByName[formatName]
  116. if !found {
  117. return nil, newError("config loader not found: ", formatName).AtWarning()
  118. }
  119. return f.Loader(input)
  120. }
  121. // loadSingleConfigAutoFormat loads a single config with from given source.
  122. // input accepts:
  123. // * string of a single filename/url(s) to open to read
  124. // * io.Reader that reads a config content (the original way)
  125. func loadSingleConfigAutoFormat(input interface{}) (*Config, error) {
  126. switch v := input.(type) {
  127. case cmdarg.Arg:
  128. return loadSingleConfigAutoFormatFromFile(v.String())
  129. case string:
  130. return loadSingleConfigByTryingAllLoaders(v)
  131. case io.Reader:
  132. data, err := buf.ReadAllToBytes(v)
  133. if err != nil {
  134. return nil, err
  135. }
  136. return loadSingleConfigByTryingAllLoaders(data)
  137. default:
  138. return loadSingleConfigByTryingAllLoaders(v)
  139. }
  140. }
  141. func loadSingleConfigAutoFormatFromFile(file string) (*Config, error) {
  142. extension := getExtension(file)
  143. if extension != "" {
  144. lowerName := strings.ToLower(extension)
  145. if f, found := configLoaderByExt[lowerName]; found {
  146. return f.Loader(file)
  147. }
  148. return nil, newError("config loader not found for: ", extension).AtWarning()
  149. }
  150. return loadSingleConfigByTryingAllLoaders(file)
  151. }
  152. func loadSingleConfigByTryingAllLoaders(input interface{}) (*Config, error) {
  153. var errorReasons strings.Builder
  154. queue := configLoaders.Clone()
  155. size := queue.Size()
  156. for i := 0; i < int(size); i++ {
  157. f, _, _ := queue.Pop()
  158. if f.Name[0] == FormatAuto {
  159. continue
  160. }
  161. c, err := f.Loader(input)
  162. if err == nil {
  163. return c, nil
  164. }
  165. errorReasons.WriteString(fmt.Sprintf("unable to parse as %v:%v;", f.Name[0], err.Error()))
  166. }
  167. return nil, newError("tried all loaders but failed when attempting to parse: ", input, ";", errorReasons.String()).AtWarning()
  168. }
  169. func getInputCount(input interface{}) int {
  170. s := reflect.Indirect(reflect.ValueOf(input))
  171. k := s.Kind()
  172. if k == reflect.Slice || k == reflect.Array {
  173. return s.Len()
  174. }
  175. return 1
  176. }
  177. func loadProtobufConfig(data []byte) (*Config, error) {
  178. config := new(Config)
  179. if err := proto.Unmarshal(data, config); err != nil {
  180. return nil, err
  181. }
  182. return config, nil
  183. }
  184. func init() {
  185. common.Must(RegisterConfigLoader(&ConfigFormat{
  186. Name: []string{FormatProtobuf, FormatProtobufShort},
  187. Extension: []string{".pb"},
  188. Loader: func(input interface{}) (*Config, error) {
  189. switch v := input.(type) {
  190. case string:
  191. r, err := cmdarg.LoadArg(v)
  192. if err != nil {
  193. return nil, err
  194. }
  195. data, err := buf.ReadAllToBytes(r)
  196. if err != nil {
  197. return nil, err
  198. }
  199. return loadProtobufConfig(data)
  200. case io.Reader:
  201. data, err := buf.ReadAllToBytes(v)
  202. if err != nil {
  203. return nil, err
  204. }
  205. return loadProtobufConfig(data)
  206. default:
  207. return nil, newError("unknown type")
  208. }
  209. },
  210. }))
  211. }