config.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. )
  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. switch v := input.(type) {
  125. case cmdarg.Arg:
  126. return loadSingleConfigAutoFormatFromFile(v.String())
  127. case string:
  128. return loadSingleConfigByTryingAllLoaders(v)
  129. case io.Reader:
  130. data, err := buf.ReadAllToBytes(v)
  131. if err != nil {
  132. return nil, err
  133. }
  134. return loadSingleConfigByTryingAllLoaders(data)
  135. default:
  136. return loadSingleConfigByTryingAllLoaders(v)
  137. }
  138. }
  139. func loadSingleConfigAutoFormatFromFile(file string) (*Config, error) {
  140. extension := getExtension(file)
  141. if extension != "" {
  142. lowerName := strings.ToLower(extension)
  143. if f, found := configLoaderByExt[lowerName]; found {
  144. return f.Loader(file)
  145. }
  146. return nil, newError("config loader not found for: ", extension).AtWarning()
  147. }
  148. return loadSingleConfigByTryingAllLoaders(file)
  149. }
  150. func loadSingleConfigByTryingAllLoaders(input interface{}) (*Config, error) {
  151. var errorReasons strings.Builder
  152. for _, f := range configLoaders {
  153. if f.Name[0] == FormatAuto {
  154. continue
  155. }
  156. c, err := f.Loader(input)
  157. if err == nil {
  158. return c, nil
  159. }
  160. errorReasons.WriteString(fmt.Sprintf("unable to parse as %v:%v;", f.Name[0], err.Error()))
  161. }
  162. return nil, newError("tried all loaders but failed when attempting to parse: ", input, ";", errorReasons.String()).AtWarning()
  163. }
  164. func getInputCount(input interface{}) int {
  165. s := reflect.Indirect(reflect.ValueOf(input))
  166. k := s.Kind()
  167. if k == reflect.Slice || k == reflect.Array {
  168. return s.Len()
  169. }
  170. return 1
  171. }
  172. func loadProtobufConfig(data []byte) (*Config, error) {
  173. config := new(Config)
  174. if err := proto.Unmarshal(data, config); err != nil {
  175. return nil, err
  176. }
  177. return config, nil
  178. }
  179. func init() {
  180. common.Must(RegisterConfigLoader(&ConfigFormat{
  181. Name: []string{FormatProtobuf, FormatProtobufShort},
  182. Extension: []string{".pb"},
  183. Loader: func(input interface{}) (*Config, error) {
  184. switch v := input.(type) {
  185. case string:
  186. r, err := cmdarg.LoadArg(v)
  187. if err != nil {
  188. return nil, err
  189. }
  190. data, err := buf.ReadAllToBytes(r)
  191. if err != nil {
  192. return nil, err
  193. }
  194. return loadProtobufConfig(data)
  195. case io.Reader:
  196. data, err := buf.ReadAllToBytes(v)
  197. if err != nil {
  198. return nil, err
  199. }
  200. return loadProtobufConfig(data)
  201. default:
  202. return nil, newError("unknown type")
  203. }
  204. },
  205. }))
  206. }