config.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. //go:build !confonly
  2. // +build !confonly
  3. package core
  4. import (
  5. "io"
  6. "path/filepath"
  7. "strings"
  8. "google.golang.org/protobuf/proto"
  9. "github.com/v2fly/v2ray-core/v4/common"
  10. "github.com/v2fly/v2ray-core/v4/common/buf"
  11. "github.com/v2fly/v2ray-core/v4/common/cmdarg"
  12. "github.com/v2fly/v2ray-core/v4/main/confloader"
  13. )
  14. // ConfigFormat is a configurable format of V2Ray config file.
  15. type ConfigFormat struct {
  16. Name []string
  17. Extension []string
  18. Loader ConfigLoader
  19. }
  20. // ConfigLoader is a utility to load V2Ray config from external source.
  21. type ConfigLoader func(input interface{}) (*Config, error)
  22. var (
  23. configLoaderByName = make(map[string]*ConfigFormat)
  24. configLoaderByExt = make(map[string]*ConfigFormat)
  25. )
  26. // RegisterConfigLoader add a new ConfigLoader.
  27. func RegisterConfigLoader(format *ConfigFormat) error {
  28. for _, name := range format.Name {
  29. lname := strings.ToLower(name)
  30. if _, found := configLoaderByName[lname]; found {
  31. return newError(name, " already registered.")
  32. }
  33. configLoaderByName[lname] = format
  34. }
  35. for _, ext := range format.Extension {
  36. lext := strings.ToLower(ext)
  37. if f, found := configLoaderByExt[lext]; found {
  38. return newError(ext, " already registered to ", f.Name)
  39. }
  40. configLoaderByExt[lext] = format
  41. }
  42. return nil
  43. }
  44. func getExtension(filename string) string {
  45. ext := filepath.Ext(filename)
  46. return strings.ToLower(ext)
  47. }
  48. // GetConfigLoader get config loader by name and filename.
  49. // Specify formatName to explicitly select a loader.
  50. // Specify filename to choose loader by detect its extension.
  51. // Leave formatName and filename blank for default loader
  52. func GetConfigLoader(formatName string, filename string) (*ConfigFormat, error) {
  53. if formatName != "" {
  54. // if explicitly specified, we can safely assume that user knows what they are
  55. if f, found := configLoaderByName[formatName]; found {
  56. return f, nil
  57. }
  58. return nil, newError("Unable to load config in ", formatName).AtWarning()
  59. }
  60. // no explicitly specified loader, extenstion detect first
  61. if ext := getExtension(filename); len(ext) > 0 {
  62. if f, found := configLoaderByExt[ext]; found {
  63. return f, nil
  64. }
  65. }
  66. // default loader
  67. if f, found := configLoaderByName["json"]; found {
  68. return f, nil
  69. }
  70. panic("default loader not found")
  71. }
  72. // LoadConfig loads config with given format from given source.
  73. // input accepts 2 different types:
  74. // * []string slice of multiple filename/url(s) to open to read
  75. // * io.Reader that reads a config content (the original way)
  76. func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) {
  77. f, err := GetConfigLoader(formatName, filename)
  78. if err != nil {
  79. return nil, err
  80. }
  81. return f.Loader(input)
  82. }
  83. func loadProtobufConfig(data []byte) (*Config, error) {
  84. config := new(Config)
  85. if err := proto.Unmarshal(data, config); err != nil {
  86. return nil, err
  87. }
  88. return config, nil
  89. }
  90. func init() {
  91. common.Must(RegisterConfigLoader(&ConfigFormat{
  92. Name: []string{"Protobuf", "pb"},
  93. Extension: []string{".pb"},
  94. Loader: func(input interface{}) (*Config, error) {
  95. switch v := input.(type) {
  96. case cmdarg.Arg:
  97. r, err := confloader.LoadConfig(v[0])
  98. if err != nil {
  99. return nil, err
  100. }
  101. data, err := buf.ReadAllToBytes(r)
  102. if err != nil {
  103. return nil, err
  104. }
  105. return loadProtobufConfig(data)
  106. case io.Reader:
  107. data, err := buf.ReadAllToBytes(v)
  108. if err != nil {
  109. return nil, err
  110. }
  111. return loadProtobufConfig(data)
  112. default:
  113. return nil, newError("unknow type")
  114. }
  115. },
  116. }))
  117. }