mirror of https://github.com/v2ray/v2ray-core
vcptr
5 years ago
19 changed files with 553 additions and 79 deletions
@ -0,0 +1,16 @@
|
||||
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 |
||||
} |
@ -0,0 +1,88 @@
|
||||
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 <url>.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 { |
||||
ctllog.Println("Read config: ", arg) |
||||
r, err := c.LoadArg(arg) |
||||
common.Must(err) |
||||
c, err := serial.DecodeJSONConfig(r) |
||||
if err != nil { |
||||
ctllog.Fatalln(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{})) |
||||
} |
@ -0,0 +1,9 @@
|
||||
package confloader |
||||
|
||||
import "v2ray.com/core/common/errors" |
||||
|
||||
type errPathObjHolder struct{} |
||||
|
||||
func newError(values ...interface{}) *errors.Error { |
||||
return errors.New(values...).WithPathObj(errPathObjHolder{}) |
||||
} |
@ -1,48 +1,86 @@
|
||||
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" |
||||
) |
||||
|
||||
//go:generate errorgen
|
||||
func ConfigLoader(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 FetchHTTPContent(target string) ([]byte, error) { |
||||
|
||||
func loadConfigFile(configFile string) (io.ReadCloser, error) { |
||||
if configFile == "stdin:" { |
||||
return os.Stdin, nil |
||||
parsedTarget, err := url.Parse(target) |
||||
if err != nil { |
||||
return nil, newError("invalid URL: ", target).Base(err) |
||||
} |
||||
|
||||
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 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 |
||||
if resp.StatusCode != 200 { |
||||
return nil, newError("unexpected HTTP status code: ", resp.StatusCode) |
||||
} |
||||
|
||||
fixedFile := os.ExpandEnv(configFile) |
||||
file, err := os.Open(fixedFile) |
||||
content, err := buf.ReadAllToBytes(resp.Body) |
||||
if err != nil { |
||||
return nil, newError("config file not readable").Base(err) |
||||
return nil, newError("failed to read HTTP response").Base(err) |
||||
} |
||||
defer file.Close() |
||||
|
||||
content, err := buf.ReadFrom(file) |
||||
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, newError("failed to load config file: ", fixedFile).Base(err).AtWarning() |
||||
return nil, err |
||||
} |
||||
return &buf.MultiBufferContainer{ |
||||
MultiBuffer: content, |
||||
}, nil |
||||
|
||||
return strings.NewReader(buf.String()), nil |
||||
} |
||||
|
||||
func init() { |
||||
confloader.EffectiveConfigFileLoader = loadConfigFile |
||||
confloader.EffectiveConfigFileLoader = ConfigLoader |
||||
confloader.EffectiveExtConfigLoader = ExtConfigLoader |
||||
} |
||||
|
@ -0,0 +1,9 @@
|
||||
package jsonem |
||||
|
||||
import "v2ray.com/core/common/errors" |
||||
|
||||
type errPathObjHolder struct{} |
||||
|
||||
func newError(values ...interface{}) *errors.Error { |
||||
return errors.New(values...).WithPathObj(errPathObjHolder{}) |
||||
} |
@ -1,15 +1,38 @@
|
||||
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: serial.LoadJSONConfig, |
||||
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") |
||||
} |
||||
}, |
||||
})) |
||||
} |
||||
|
Loading…
Reference in new issue