config.go 5.6 KB

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