diff --git a/common/cmdarg/cmdarg.go b/common/cmdarg/cmdarg.go deleted file mode 100644 index f524eb37d..000000000 --- a/common/cmdarg/cmdarg.go +++ /dev/null @@ -1,16 +0,0 @@ -package cmdarg - -import "strings" - -// Arg is used by flag to accept multiple argument. -type Arg []string - -func (c *Arg) String() string { - return strings.Join([]string(*c), " ") -} - -// Set is the method flag package calls -func (c *Arg) Set(value string) error { - *c = append(*c, value) - return nil -} diff --git a/common/log/logger.go b/common/log/logger.go index a6b8316b1..9a2591cb9 100644 --- a/common/log/logger.go +++ b/common/log/logger.go @@ -119,15 +119,6 @@ func CreateStdoutLogWriter() WriterCreator { } } -// CreateStderrLogWriter returns a LogWriterCreator that creates LogWriter for stderr. -func CreateStderrLogWriter() WriterCreator { - return func() Writer { - return &consoleLogWriter{ - logger: log.New(os.Stderr, "", log.Ldate|log.Ltime), - } - } -} - // CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file. func CreateFileLogWriter(path string) (WriterCreator, error) { file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) diff --git a/common/platform/ctlcmd/ctlcmd.go b/common/platform/ctlcmd/ctlcmd.go index adebbf7a0..ed770b555 100644 --- a/common/platform/ctlcmd/ctlcmd.go +++ b/common/platform/ctlcmd/ctlcmd.go @@ -40,10 +40,5 @@ func Run(args []string, input io.Reader) (buf.MultiBuffer, error) { return nil, newError(msg).Base(err) } - // log stderr, info message - if !errBuffer.IsEmpty() { - newError("v2ctl > \n", errBuffer.MultiBuffer.String()).AtInfo().WriteToLog() - } - return outBuffer.MultiBuffer, nil } diff --git a/config.go b/config.go index bcadbb18b..d328a87bd 100755 --- a/config.go +++ b/config.go @@ -9,8 +9,6 @@ import ( "github.com/golang/protobuf/proto" "v2ray.com/core/common" "v2ray.com/core/common/buf" - "v2ray.com/core/common/cmdarg" - "v2ray.com/core/main/confloader" ) // ConfigFormat is a configurable format of V2Ray config file. @@ -21,7 +19,7 @@ type ConfigFormat struct { } // ConfigLoader is a utility to load V2Ray config from external source. -type ConfigLoader func(input interface{}) (*Config, error) +type ConfigLoader func(input io.Reader) (*Config, error) var ( configLoaderByName = make(map[string]*ConfigFormat) @@ -56,10 +54,7 @@ func getExtension(filename string) string { } // LoadConfig loads config with given format from given source. -// input accepts 2 different types: -// * []string slice of multiple filename/url(s) to open to read -// * io.Reader that reads a config content (the original way) -func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) { +func LoadConfig(formatName string, filename string, input io.Reader) (*Config, error) { ext := getExtension(filename) if len(ext) > 0 { if f, found := configLoaderByExt[ext]; found { @@ -74,8 +69,12 @@ func LoadConfig(formatName string, filename string, input interface{}) (*Config, return nil, newError("Unable to load config in ", formatName).AtWarning() } -func loadProtobufConfig(data []byte) (*Config, error) { +func loadProtobufConfig(input io.Reader) (*Config, error) { config := new(Config) + data, err := buf.ReadAllToBytes(input) + if err != nil { + return nil, err + } if err := proto.Unmarshal(data, config); err != nil { return nil, err } @@ -86,21 +85,6 @@ func init() { common.Must(RegisterConfigLoader(&ConfigFormat{ Name: "Protobuf", Extension: []string{"pb"}, - Loader: func(input interface{}) (*Config, error) { - switch v := input.(type) { - case cmdarg.Arg: - r, err := confloader.LoadConfig(v[0]) - common.Must(err) - data, err := buf.ReadAllToBytes(r) - common.Must(err) - return loadProtobufConfig(data) - case io.Reader: - data, err := buf.ReadAllToBytes(v) - common.Must(err) - return loadProtobufConfig(data) - default: - return nil, newError("unknow type") - } - }, + Loader: loadProtobufConfig, })) } diff --git a/infra/conf/command/command.go b/infra/conf/command/command.go new file mode 100644 index 000000000..9f8c12d56 --- /dev/null +++ b/infra/conf/command/command.go @@ -0,0 +1,48 @@ +package command + +//go:generate errorgen + +import ( + "os" + + "github.com/golang/protobuf/proto" + "v2ray.com/core/common" + "v2ray.com/core/infra/conf/serial" + "v2ray.com/core/infra/control" +) + +type ConfigCommand struct{} + +func (c *ConfigCommand) Name() string { + return "config" +} + +func (c *ConfigCommand) Description() control.Description { + return control.Description{ + Short: "Convert config among different formats.", + Usage: []string{ + "v2ctl config", + }, + } +} + +func (c *ConfigCommand) Execute(args []string) error { + pbConfig, err := serial.LoadJSONConfig(os.Stdin) + if err != nil { + return newError("failed to parse json config").Base(err) + } + + bytesConfig, err := proto.Marshal(pbConfig) + if err != nil { + return newError("failed to marshal proto config").Base(err) + } + + if _, err := os.Stdout.Write(bytesConfig); err != nil { + return newError("failed to write proto config").Base(err) + } + return nil +} + +func init() { + common.Must(control.RegisterCommand(&ConfigCommand{})) +} diff --git a/main/jsonem/errors.generated.go b/infra/conf/command/errors.generated.go similarity index 92% rename from main/jsonem/errors.generated.go rename to infra/conf/command/errors.generated.go index 48961bcea..66f780510 100644 --- a/main/jsonem/errors.generated.go +++ b/infra/conf/command/errors.generated.go @@ -1,4 +1,4 @@ -package jsonem +package command import "v2ray.com/core/common/errors" diff --git a/infra/conf/serial/loader.go b/infra/conf/serial/loader.go index f7ecf2db1..1d084778d 100644 --- a/infra/conf/serial/loader.go +++ b/infra/conf/serial/loader.go @@ -38,9 +38,7 @@ func findOffset(b []byte, o int) *offset { return &offset{line: line, char: char} } -// DecodeJSONConfig reads from reader and decode the config into *conf.Config -// syntax error could be detected. -func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) { +func LoadJSONConfig(reader io.Reader) (*core.Config, error) { jsonConfig := &conf.Config{} jsonContent := bytes.NewBuffer(make([]byte, 0, 10240)) @@ -64,15 +62,6 @@ func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) { return nil, newError("failed to read config file").Base(err) } - return jsonConfig, nil -} - -func LoadJSONConfig(reader io.Reader) (*core.Config, error) { - jsonConfig, err := DecodeJSONConfig(reader) - if err != nil { - return nil, err - } - pbConfig, err := jsonConfig.Build() if err != nil { return nil, newError("failed to parse json config").Base(err) diff --git a/infra/conf/v2ray.go b/infra/conf/v2ray.go index b87447231..58c9c6dad 100644 --- a/infra/conf/v2ray.go +++ b/infra/conf/v2ray.go @@ -304,94 +304,6 @@ type Config struct { Reverse *ReverseConfig `json:"reverse"` } -func (c *Config) findInboundTag(tag string) int { - found := -1 - for idx, ib := range c.InboundConfigs { - if ib.Tag == tag { - found = idx - break - } - } - return found -} - -func (c *Config) findOutboundTag(tag string) int { - found := -1 - for idx, ob := range c.OutboundConfigs { - if ob.Tag == tag { - found = idx - break - } - } - return found -} - -// Override method accepts another Config overrides the current attribute -func (c *Config) Override(o *Config, fn string) { - - // only process the non-deprecated members - - if o.LogConfig != nil { - c.LogConfig = o.LogConfig - } - if o.RouterConfig != nil { - c.RouterConfig = o.RouterConfig - } - if o.DNSConfig != nil { - c.DNSConfig = o.DNSConfig - } - if o.Transport != nil { - c.Transport = o.Transport - } - if o.Policy != nil { - c.Policy = o.Policy - } - if o.Api != nil { - c.Api = o.Api - } - if o.Stats != nil { - c.Stats = o.Stats - } - if o.Reverse != nil { - c.Reverse = o.Reverse - } - - // update the Inbound in slice if the only one in overide config has same tag - if len(o.InboundConfigs) > 0 { - if len(c.InboundConfigs) > 0 && len(o.InboundConfigs) == 1 { - if idx := c.findInboundTag(o.InboundConfigs[0].Tag); idx > -1 { - c.InboundConfigs[idx] = o.InboundConfigs[0] - newError("<", fn, "> updated inbound with tag: ", o.InboundConfigs[0].Tag).AtInfo().WriteToLog() - } else { - c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[0]) - newError("<", fn, "> appended inbound with tag: ", o.InboundConfigs[0].Tag).AtInfo().WriteToLog() - } - } else { - c.InboundConfigs = o.InboundConfigs - } - } - - // update the Outbound in slice if the only one in overide config has same tag - if len(o.OutboundConfigs) > 0 { - if len(c.OutboundConfigs) > 0 && len(o.OutboundConfigs) == 1 { - if idx := c.findOutboundTag(o.OutboundConfigs[0].Tag); idx > -1 { - c.OutboundConfigs[idx] = o.OutboundConfigs[0] - newError("<", fn, "> updated outbound with tag: ", o.OutboundConfigs[0].Tag).AtInfo().WriteToLog() - } else { - if strings.Contains(strings.ToLower(fn), "tail") { - c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[0]) - newError("<", fn, "> appended outbound with tag: ", o.OutboundConfigs[0].Tag).AtInfo().WriteToLog() - } else { - c.OutboundConfigs = append(o.OutboundConfigs, c.OutboundConfigs...) - newError("<", fn, "> prepended outbound with tag: ", o.OutboundConfigs[0].Tag).AtInfo().WriteToLog() - } - } - } else { - c.OutboundConfigs = o.OutboundConfigs - } - } -} - func applyTransportConfig(s *StreamConfig, t *TransportConfig) { if s.TCPSettings == nil { s.TCPSettings = t.TCPConfig diff --git a/infra/conf/v2ray_test.go b/infra/conf/v2ray_test.go index 8c59a7ae8..d324d3ea6 100644 --- a/infra/conf/v2ray_test.go +++ b/infra/conf/v2ray_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/golang/protobuf/proto" - "github.com/google/go-cmp/cmp" "v2ray.com/core" "v2ray.com/core/app/dispatcher" "v2ray.com/core/app/log" @@ -370,80 +369,3 @@ func TestMuxConfig_Build(t *testing.T) { }) } } - -func TestConfig_Override(t *testing.T) { - tests := []struct { - name string - orig *Config - over *Config - fn string - want *Config - }{ - {"combine/empty", - &Config{}, - &Config{ - LogConfig: &LogConfig{}, - RouterConfig: &RouterConfig{}, - DNSConfig: &DnsConfig{}, - Transport: &TransportConfig{}, - Policy: &PolicyConfig{}, - Api: &ApiConfig{}, - Stats: &StatsConfig{}, - Reverse: &ReverseConfig{}, - }, - "", - &Config{ - LogConfig: &LogConfig{}, - RouterConfig: &RouterConfig{}, - DNSConfig: &DnsConfig{}, - Transport: &TransportConfig{}, - Policy: &PolicyConfig{}, - Api: &ApiConfig{}, - Stats: &StatsConfig{}, - Reverse: &ReverseConfig{}, - }, - }, - {"combine/newattr", - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "old"}}}, - &Config{LogConfig: &LogConfig{}}, "", - &Config{LogConfig: &LogConfig{}, InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "old"}}}}, - {"replace/inbounds", - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "pos0"}, InboundDetourConfig{Protocol: "vmess", Tag: "pos1"}}}, - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "pos1", Protocol: "kcp"}}}, - "", - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "pos0"}, InboundDetourConfig{Tag: "pos1", Protocol: "kcp"}}}}, - {"replace/inbounds-replaceall", - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "pos0"}, InboundDetourConfig{Protocol: "vmess", Tag: "pos1"}}}, - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "pos1", Protocol: "kcp"}, InboundDetourConfig{Tag: "pos2", Protocol: "kcp"}}}, - "", - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "pos1", Protocol: "kcp"}, InboundDetourConfig{Tag: "pos2", Protocol: "kcp"}}}}, - {"replace/notag-append", - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{}, InboundDetourConfig{Protocol: "vmess"}}}, - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{Tag: "pos1", Protocol: "kcp"}}}, - "", - &Config{InboundConfigs: []InboundDetourConfig{InboundDetourConfig{}, InboundDetourConfig{Protocol: "vmess"}, InboundDetourConfig{Tag: "pos1", Protocol: "kcp"}}}}, - {"replace/outbounds", - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos0"}, OutboundDetourConfig{Protocol: "vmess", Tag: "pos1"}}}, - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos1", Protocol: "kcp"}}}, - "", - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos0"}, OutboundDetourConfig{Tag: "pos1", Protocol: "kcp"}}}}, - {"replace/outbounds-prepend", - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos0"}, OutboundDetourConfig{Protocol: "vmess", Tag: "pos1"}}}, - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos1", Protocol: "kcp"}, OutboundDetourConfig{Tag: "pos2", Protocol: "kcp"}}}, - "config.json", - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos1", Protocol: "kcp"}, OutboundDetourConfig{Tag: "pos2", Protocol: "kcp"}}}}, - {"replace/outbounds-append", - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos0"}, OutboundDetourConfig{Protocol: "vmess", Tag: "pos1"}}}, - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos2", Protocol: "kcp"}}}, - "config_tail.json", - &Config{OutboundConfigs: []OutboundDetourConfig{OutboundDetourConfig{Tag: "pos0"}, OutboundDetourConfig{Protocol: "vmess", Tag: "pos1"}, OutboundDetourConfig{Tag: "pos2", Protocol: "kcp"}}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.orig.Override(tt.over, tt.fn) - if r := cmp.Diff(tt.orig, tt.want); r != "" { - t.Error(r) - } - }) - } -} diff --git a/infra/control/config.go b/infra/control/config.go deleted file mode 100644 index 02d7cd5dd..000000000 --- a/infra/control/config.go +++ /dev/null @@ -1,86 +0,0 @@ -package control - -import ( - "bytes" - "io" - "io/ioutil" - "os" - "strings" - - "github.com/golang/protobuf/proto" - "v2ray.com/core/common" - "v2ray.com/core/infra/conf" - "v2ray.com/core/infra/conf/serial" -) - -// ConfigCommand is the json to pb convert struct -type ConfigCommand struct{} - -// Name for cmd usage -func (c *ConfigCommand) Name() string { - return "config" -} - -// Description for help usage -func (c *ConfigCommand) Description() Description { - return Description{ - Short: "merge multiple json config", - Usage: []string{"v2ctl config config.json c1.json c2.json .json"}, - } -} - -// Execute real work here. -func (c *ConfigCommand) Execute(args []string) error { - if len(args) < 1 { - return newError("empty config list") - } - - conf := &conf.Config{} - for _, arg := range args { - newError("Reading config: ", arg).AtInfo().WriteToLog() - r, err := c.LoadArg(arg) - common.Must(err) - c, err := serial.DecodeJSONConfig(r) - common.Must(err) - conf.Override(c, arg) - } - - pbConfig, err := conf.Build() - if err != nil { - return err - } - - bytesConfig, err := proto.Marshal(pbConfig) - if err != nil { - return newError("failed to marshal proto config").Base(err) - } - - if _, err := os.Stdout.Write(bytesConfig); err != nil { - return newError("failed to write proto config").Base(err) - } - - return nil -} - -// LoadArg loads one arg, maybe an remote url, or local file path -func (c *ConfigCommand) LoadArg(arg string) (out io.Reader, err error) { - - var data []byte - if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") { - data, err = FetchHTTPContent(arg) - } else if arg == "stdin:" { - data, err = ioutil.ReadAll(os.Stdin) - } else { - data, err = ioutil.ReadFile(arg) - } - - if err != nil { - return - } - out = bytes.NewBuffer(data) - return -} - -func init() { - common.Must(RegisterCommand(&ConfigCommand{})) -} diff --git a/infra/control/fetch.go b/infra/control/fetch.go index a9deef25f..167c7483c 100644 --- a/infra/control/fetch.go +++ b/infra/control/fetch.go @@ -5,7 +5,6 @@ import ( "net/url" "os" "strings" - "time" "v2ray.com/core/common" "v2ray.com/core/common/buf" @@ -24,54 +23,46 @@ func (c *FetchCommand) Description() Description { } } +func (c *FetchCommand) isValidScheme(scheme string) bool { + scheme = strings.ToLower(scheme) + return scheme == "http" || scheme == "https" +} + func (c *FetchCommand) Execute(args []string) error { if len(args) < 1 { return newError("empty url") } - content, err := FetchHTTPContent(args[0]) - if err != nil { - return newError("failed to read HTTP response").Base(err) - } - - os.Stdout.Write(content) - return nil -} - -// FetchHTTPContent dials https for remote content -func FetchHTTPContent(target string) ([]byte, error) { - + target := args[0] parsedTarget, err := url.Parse(target) if err != nil { - return nil, newError("invalid URL: ", target).Base(err) + return newError("invalid URL: ", target).Base(err) + } + if !c.isValidScheme(parsedTarget.Scheme) { + return newError("invalid scheme: ", parsedTarget.Scheme) } - if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" { - return nil, newError("invalid scheme: ", parsedTarget.Scheme) - } - - client := &http.Client{ - Timeout: 30 * time.Second, - } + client := &http.Client{} resp, err := client.Do(&http.Request{ Method: "GET", URL: parsedTarget, Close: true, }) if err != nil { - return nil, newError("failed to dial to ", target).Base(err) + return newError("failed to dial to ", target).Base(err) } - defer resp.Body.Close() if resp.StatusCode != 200 { - return nil, newError("unexpected HTTP status code: ", resp.StatusCode) + return newError("unexpected HTTP status code: ", resp.StatusCode) } content, err := buf.ReadAllToBytes(resp.Body) if err != nil { - return nil, newError("failed to read HTTP response").Base(err) + return newError("failed to read HTTP response").Base(err) } - return content, nil + os.Stdout.Write(content) + + return nil } func init() { diff --git a/infra/control/main/main.go b/infra/control/main/main.go index e78fa5d12..5c41aeb0e 100644 --- a/infra/control/main/main.go +++ b/infra/control/main/main.go @@ -5,8 +5,7 @@ import ( "fmt" "os" - commlog "v2ray.com/core/common/log" - // _ "v2ray.com/core/infra/conf/command" + _ "v2ray.com/core/infra/conf/command" "v2ray.com/core/infra/control" ) @@ -18,8 +17,6 @@ func getCommandName() string { } func main() { - // let the v2ctl prints log at stderr - commlog.RegisterHandler(commlog.NewLogger(commlog.CreateStderrLogWriter())) name := getCommandName() cmd := control.GetCommand(name) if cmd == nil { diff --git a/main/confloader/confloader.go b/main/confloader/confloader.go index d52b5b89e..9edf6909e 100644 --- a/main/confloader/confloader.go +++ b/main/confloader/confloader.go @@ -5,30 +5,15 @@ import ( "os" ) -type configFileLoader func(string) (io.Reader, error) -type extconfigLoader func([]string) (io.Reader, error) +type configFileLoader func(string) (io.ReadCloser, error) var ( EffectiveConfigFileLoader configFileLoader - EffectiveExtConfigLoader extconfigLoader ) -// LoadConfig reads from a path/url/stdin -// actual work is in external module -func LoadConfig(file string) (io.Reader, error) { +func LoadConfig(file string) (io.ReadCloser, error) { if EffectiveConfigFileLoader == nil { - newError("external config module not loaded, reading from stdin").AtInfo().WriteToLog() return os.Stdin, nil } return EffectiveConfigFileLoader(file) } - -// LoadExtConfig calls v2ctl to handle multiple config -// the actual work also in external module -func LoadExtConfig(files []string) (io.Reader, error) { - if EffectiveExtConfigLoader == nil { - return nil, newError("external config module not loaded").AtError() - } - - return EffectiveExtConfigLoader(files) -} diff --git a/main/confloader/errors.generated.go b/main/confloader/errors.generated.go deleted file mode 100644 index deda6e515..000000000 --- a/main/confloader/errors.generated.go +++ /dev/null @@ -1,9 +0,0 @@ -package confloader - -import "v2ray.com/core/common/errors" - -type errPathObjHolder struct{} - -func newError(values ...interface{}) *errors.Error { - return errors.New(values...).WithPathObj(errPathObjHolder{}) -} diff --git a/main/confloader/external/external.go b/main/confloader/external/external.go index a16f0c7a5..75dcdea37 100644 --- a/main/confloader/external/external.go +++ b/main/confloader/external/external.go @@ -1,86 +1,48 @@ package external -//go:generate errorgen - import ( - "bytes" "io" - "io/ioutil" - "net/http" - "net/url" "os" "strings" - "time" "v2ray.com/core/common/buf" "v2ray.com/core/common/platform/ctlcmd" "v2ray.com/core/main/confloader" ) -func ConfigLoader(arg string) (out io.Reader, err error) { +//go:generate errorgen - var data []byte - if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") { - data, err = FetchHTTPContent(arg) - } else if arg == "stdin:" { - data, err = ioutil.ReadAll(os.Stdin) - } else { - data, err = ioutil.ReadFile(arg) +func loadConfigFile(configFile string) (io.ReadCloser, error) { + if configFile == "stdin:" { + return os.Stdin, nil } + if strings.HasPrefix(configFile, "http://") || strings.HasPrefix(configFile, "https://") { + content, err := ctlcmd.Run([]string{"fetch", configFile}, nil) + if err != nil { + return nil, err + } + return &buf.MultiBufferContainer{ + MultiBuffer: content, + }, nil + } + + fixedFile := os.ExpandEnv(configFile) + file, err := os.Open(fixedFile) if err != nil { - return + return nil, newError("config file not readable").Base(err) } - out = bytes.NewBuffer(data) - return -} + defer file.Close() -func FetchHTTPContent(target string) ([]byte, error) { - - parsedTarget, err := url.Parse(target) + content, err := buf.ReadFrom(file) if err != nil { - return nil, newError("invalid URL: ", target).Base(err) + return nil, newError("failed to load config file: ", fixedFile).Base(err).AtWarning() } - - if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" { - return nil, newError("invalid scheme: ", parsedTarget.Scheme) - } - - client := &http.Client{ - Timeout: 30 * time.Second, - } - resp, err := client.Do(&http.Request{ - Method: "GET", - URL: parsedTarget, - Close: true, - }) - if err != nil { - return nil, newError("failed to dial to ", target).Base(err) - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return nil, newError("unexpected HTTP status code: ", resp.StatusCode) - } - - content, err := buf.ReadAllToBytes(resp.Body) - if err != nil { - return nil, newError("failed to read HTTP response").Base(err) - } - - return content, nil -} - -func ExtConfigLoader(files []string) (io.Reader, error) { - buf, err := ctlcmd.Run(append([]string{"config"}, files...), os.Stdin) - if err != nil { - return nil, err - } - - return strings.NewReader(buf.String()), nil + return &buf.MultiBufferContainer{ + MultiBuffer: content, + }, nil } func init() { - confloader.EffectiveConfigFileLoader = ConfigLoader - confloader.EffectiveExtConfigLoader = ExtConfigLoader + confloader.EffectiveConfigFileLoader = loadConfigFile } diff --git a/main/json/config_json.go b/main/json/config_json.go index 59bcc63d0..45c6374d9 100644 --- a/main/json/config_json.go +++ b/main/json/config_json.go @@ -7,28 +7,22 @@ import ( "v2ray.com/core" "v2ray.com/core/common" - "v2ray.com/core/common/cmdarg" - "v2ray.com/core/infra/conf/serial" - "v2ray.com/core/main/confloader" + "v2ray.com/core/common/buf" + "v2ray.com/core/common/platform/ctlcmd" ) func init() { common.Must(core.RegisterConfigLoader(&core.ConfigFormat{ Name: "JSON", Extension: []string{"json"}, - Loader: func(input interface{}) (*core.Config, error) { - switch v := input.(type) { - case cmdarg.Arg: - r, err := confloader.LoadExtConfig(v) - if err != nil { - return nil, newError("failed to execute v2ctl to convert config file.").Base(err).AtWarning() - } - return core.LoadConfig("protobuf", "", r) - case io.Reader: - return serial.LoadJSONConfig(v) - default: - return nil, newError("unknow type") + Loader: func(input io.Reader) (*core.Config, error) { + jsonContent, err := ctlcmd.Run([]string{"config"}, input) + if err != nil { + return nil, newError("failed to execute v2ctl to convert config file.").Base(err).AtWarning() } + return core.LoadConfig("protobuf", "", &buf.MultiBufferContainer{ + MultiBuffer: jsonContent, + }) }, })) } diff --git a/main/jsonem/jsonem.go b/main/jsonem/jsonem.go index 6000e2853..7b58bb9b5 100644 --- a/main/jsonem/jsonem.go +++ b/main/jsonem/jsonem.go @@ -1,38 +1,15 @@ package jsonem import ( - "io" - "v2ray.com/core" "v2ray.com/core/common" - "v2ray.com/core/common/cmdarg" - "v2ray.com/core/infra/conf" "v2ray.com/core/infra/conf/serial" - "v2ray.com/core/main/confloader" ) func init() { common.Must(core.RegisterConfigLoader(&core.ConfigFormat{ Name: "JSON", Extension: []string{"json"}, - Loader: func(input interface{}) (*core.Config, error) { - switch v := input.(type) { - case cmdarg.Arg: - cf := &conf.Config{} - for _, arg := range v { - newError("Reading config: ", arg).AtInfo().WriteToLog() - r, err := confloader.LoadConfig(arg) - common.Must(err) - c, err := serial.DecodeJSONConfig(r) - common.Must(err) - cf.Override(c, arg) - } - return cf.Build() - case io.Reader: - return serial.LoadJSONConfig(v) - default: - return nil, newError("unknow type") - } - }, + Loader: serial.LoadJSONConfig, })) } diff --git a/main/main.go b/main/main.go index 94ba7088d..a5d3a447d 100644 --- a/main/main.go +++ b/main/main.go @@ -13,17 +13,16 @@ import ( "syscall" "v2ray.com/core" - "v2ray.com/core/common/cmdarg" "v2ray.com/core/common/platform" + "v2ray.com/core/main/confloader" _ "v2ray.com/core/main/distro/all" ) var ( - configFiles cmdarg.Arg // "Config file for V2Ray.", the option is customed type, parse in main - version = flag.Bool("version", false, "Show current version of V2Ray.") - test = flag.Bool("test", false, "Test config file only, without launching V2Ray server.") - format = flag.String("format", "json", "Format of input file.") - errNoConfig = newError("no valid config") + configFile = flag.String("config", "", "Config file for V2Ray.") + version = flag.Bool("version", false, "Show current version of V2Ray.") + test = flag.Bool("test", false, "Test config file only, without launching V2Ray server.") + format = flag.String("format", "json", "Format of input file.") ) func fileExists(file string) bool { @@ -31,23 +30,23 @@ func fileExists(file string) bool { return err == nil && !info.IsDir() } -func getConfigFilePath() (cmdarg.Arg, error) { - if len(configFiles) > 0 { - return configFiles, nil +func getConfigFilePath() string { + if len(*configFile) > 0 { + return *configFile } if workingDir, err := os.Getwd(); err == nil { configFile := filepath.Join(workingDir, "config.json") if fileExists(configFile) { - return cmdarg.Arg{configFile}, nil + return configFile } } if configFile := platform.GetConfigurationPath(); fileExists(configFile) { - return cmdarg.Arg{configFile}, nil + return configFile } - return cmdarg.Arg{"stdin:"}, nil + return "" } func GetConfigFormat() string { @@ -60,14 +59,16 @@ func GetConfigFormat() string { } func startV2Ray() (core.Server, error) { - configFiles, err := getConfigFilePath() + configFile := getConfigFilePath() + configInput, err := confloader.LoadConfig(configFile) if err != nil { - return nil, err + return nil, newError("failed to load config: ", configFile).Base(err) } + defer configInput.Close() - config, err := core.LoadConfig(GetConfigFormat(), configFiles[0], configFiles) + config, err := core.LoadConfig(GetConfigFormat(), configFile, configInput) if err != nil { - return nil, newError("failed to read config files: [", configFiles.String(), "]").Base(err) + return nil, newError("failed to read config file: ", configFile).Base(err) } server, err := core.New(config) @@ -86,8 +87,6 @@ func printVersion() { } func main() { - flag.Var(&configFiles, "config", "Config file for V2Ray. Multiple assign is accepted (only json). Latter ones overrides the former ones.") - flag.Var(&configFiles, "c", "short alias of -config") flag.Parse() printVersion() @@ -100,9 +99,6 @@ func main() { if err != nil { fmt.Println(err.Error()) // Configuration error. Exit with a special value to prevent systemd from restarting. - if err == errNoConfig { - flag.PrintDefaults() - } os.Exit(23) }