config.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package core
  2. import (
  3. "io"
  4. "strings"
  5. "github.com/golang/protobuf/proto"
  6. "github.com/xtls/xray-core/common"
  7. "github.com/xtls/xray-core/common/buf"
  8. "github.com/xtls/xray-core/common/cmdarg"
  9. "github.com/xtls/xray-core/main/confloader"
  10. )
  11. // ConfigFormat is a configurable format of Xray config file.
  12. type ConfigFormat struct {
  13. Name string
  14. Extension []string
  15. Loader ConfigLoader
  16. }
  17. // ConfigLoader is a utility to load Xray config from external source.
  18. type ConfigLoader func(input interface{}) (*Config, error)
  19. var (
  20. configLoaderByName = make(map[string]*ConfigFormat)
  21. configLoaderByExt = make(map[string]*ConfigFormat)
  22. )
  23. // RegisterConfigLoader add a new ConfigLoader.
  24. func RegisterConfigLoader(format *ConfigFormat) error {
  25. name := strings.ToLower(format.Name)
  26. if _, found := configLoaderByName[name]; found {
  27. return newError(format.Name, " already registered.")
  28. }
  29. configLoaderByName[name] = format
  30. for _, ext := range format.Extension {
  31. lext := strings.ToLower(ext)
  32. if f, found := configLoaderByExt[lext]; found {
  33. return newError(ext, " already registered to ", f.Name)
  34. }
  35. configLoaderByExt[lext] = format
  36. }
  37. return nil
  38. }
  39. func getExtension(filename string) string {
  40. idx := strings.LastIndexByte(filename, '.')
  41. if idx == -1 {
  42. return ""
  43. }
  44. return filename[idx+1:]
  45. }
  46. // LoadConfig loads config with given format from given source.
  47. // input accepts 2 different types:
  48. // * []string slice of multiple filename/url(s) to open to read
  49. // * io.Reader that reads a config content (the original way)
  50. func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) {
  51. ext := getExtension(filename)
  52. if len(ext) > 0 {
  53. if f, found := configLoaderByExt[ext]; found {
  54. return f.Loader(input)
  55. }
  56. }
  57. if f, found := configLoaderByName[formatName]; found {
  58. return f.Loader(input)
  59. }
  60. return nil, newError("Unable to load config in ", formatName).AtWarning()
  61. }
  62. func loadProtobufConfig(data []byte) (*Config, error) {
  63. config := new(Config)
  64. if err := proto.Unmarshal(data, config); err != nil {
  65. return nil, err
  66. }
  67. return config, nil
  68. }
  69. func init() {
  70. common.Must(RegisterConfigLoader(&ConfigFormat{
  71. Name: "Protobuf",
  72. Extension: []string{"pb"},
  73. Loader: func(input interface{}) (*Config, error) {
  74. switch v := input.(type) {
  75. case cmdarg.Arg:
  76. r, err := confloader.LoadConfig(v[0])
  77. common.Must(err)
  78. data, err := buf.ReadAllToBytes(r)
  79. common.Must(err)
  80. return loadProtobufConfig(data)
  81. case io.Reader:
  82. data, err := buf.ReadAllToBytes(v)
  83. common.Must(err)
  84. return loadProtobufConfig(data)
  85. default:
  86. return nil, newError("unknow type")
  87. }
  88. },
  89. }))
  90. }