github.com/v2fly/v2ray-core/v4@v4.45.2/config.go (about)

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