mirror of
https://github.com/v2ray/v2ray-core.git
synced 2026-11-02 16:21:05 +00:00
19 files changed
+557
-83
No files matched your search
@@ -2,6 +2,8 @@ package control
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -18,6 +20,7 @@ type Command interface {
|
||||
|
||||
var (
|
||||
commandRegistry = make(map[string]Command)
|
||||
ctllog = log.New(os.Stderr, "v2ctl> ", 0)
|
||||
)
|
||||
|
||||
func RegisterCommand(cmd Command) error {
|
||||
|
||||
@@ -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{}))
|
||||
}
|
||||
+27
-18
@@ -5,6 +5,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
@@ -23,46 +24,54 @@ 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")
|
||||
}
|
||||
target := args[0]
|
||||
parsedTarget, err := url.Parse(target)
|
||||
content, err := FetchHTTPContent(args[0])
|
||||
if err != nil {
|
||||
return newError("invalid URL: ", target).Base(err)
|
||||
}
|
||||
if !c.isValidScheme(parsedTarget.Scheme) {
|
||||
return newError("invalid scheme: ", parsedTarget.Scheme)
|
||||
return newError("failed to read HTTP response").Base(err)
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
os.Stdout.Write(content)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchHTTPContent dials https for remote content
|
||||
func FetchHTTPContent(target string) ([]byte, error) {
|
||||
|
||||
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 newError("failed to dial to ", target).Base(err)
|
||||
return nil, newError("failed to dial to ", target).Base(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return newError("unexpected HTTP status code: ", resp.StatusCode)
|
||||
return nil, newError("unexpected HTTP status code: ", resp.StatusCode)
|
||||
}
|
||||
|
||||
content, err := buf.ReadAllToBytes(resp.Body)
|
||||
if err != nil {
|
||||
return newError("failed to read HTTP response").Base(err)
|
||||
return nil, newError("failed to read HTTP response").Base(err)
|
||||
}
|
||||
|
||||
os.Stdout.Write(content)
|
||||
|
||||
return nil
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "v2ray.com/core/infra/conf/command"
|
||||
commlog "v2ray.com/core/common/log"
|
||||
// _ "v2ray.com/core/infra/conf/command"
|
||||
"v2ray.com/core/infra/control"
|
||||
)
|
||||
|
||||
@@ -17,6 +18,8 @@ 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 {
|
||||
|
||||
Reference in new issue
Block a user