Skip to content

Commit

Permalink
go style commands, merge v2ctl commands, v5 oriented (v2fly#369)
Browse files Browse the repository at this point in the history
* go style commands
merge v2ctl commandsw

* migrate go style commands to v2ctl

* fixes & code optimize

* sort the commands

* update commands description

* restore old proto
golang.org proto has removed UnmarshalText, without alternative

* add test command

* remove unused code

* code optimize and fix
* The commit simplifies the run and test commands code,
* Fixes a hidden issue that the format flag not applied in command "v2ray test -format=pb ..."

* fix default loader logic
  • Loading branch information
qjebbs authored Nov 23, 2020
1 parent bb74ef9 commit 521120d
Show file tree
Hide file tree
Showing 33 changed files with 1,207 additions and 854 deletions.
79 changes: 33 additions & 46 deletions infra/control/api.go → commands/all/api.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package control
package all

import (
"context"
"errors"
"flag"
"fmt"
"strings"
"time"
Expand All @@ -13,71 +12,66 @@ import (

logService "v2ray.com/core/app/log/command"
statsService "v2ray.com/core/app/stats/command"
"v2ray.com/core/common"
"v2ray.com/core/commands/base"
)

type APICommand struct{}
// cmdAPI calls an API in an V2Ray process
var cmdAPI = &base.Command{
UsageLine: "{{.Exec}} api [-server 127.0.0.1:8080] <action> <parameter>",
Short: "Call V2Ray API",
Long: `
Call V2Ray API, API calls in this command have a timeout to the server of 3 seconds.
func (c *APICommand) Name() string {
return "api"
}
The following methods are currently supported:
func (c *APICommand) Description() Description {
return Description{
Short: "Call V2Ray API",
Usage: []string{
"v2ctl api [--server=127.0.0.1:8080] Service.Method Request",
"Call an API in an V2Ray process.",
"The following methods are currently supported:",
"\tLoggerService.RestartLogger",
"\tStatsService.GetStats",
"\tStatsService.QueryStats",
"API calls in this command have a timeout to the server of 3 seconds.",
"Examples:",
"v2ctl api --server=127.0.0.1:8080 LoggerService.RestartLogger '' ",
"v2ctl api --server=127.0.0.1:8080 StatsService.QueryStats 'pattern: \"\" reset: false'",
"v2ctl api --server=127.0.0.1:8080 StatsService.GetStats 'name: \"inbound>>>statin>>>traffic>>>downlink\" reset: false'",
"v2ctl api --server=127.0.0.1:8080 StatsService.GetSysStats ''",
},
}
}
LoggerService.RestartLogger
StatsService.GetStats
StatsService.QueryStats
func (c *APICommand) Execute(args []string) error {
fs := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
Examples:
serverAddrPtr := fs.String("server", "127.0.0.1:8080", "Server address")
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 LoggerService.RestartLogger ''
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 StatsService.QueryStats 'pattern: "" reset: false'
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 StatsService.GetStats 'name: "inbound>>>statin>>>traffic>>>downlink" reset: false'
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 StatsService.GetSysStats ''
`,
}

if err := fs.Parse(args); err != nil {
return err
}
func init() {
cmdAPI.Run = executeAPI // break init loop
}

unnamedArgs := fs.Args()
var (
apiServerAddrPtr = cmdAPI.Flag.String("server", "127.0.0.1:8080", "")
)

func executeAPI(cmd *base.Command, args []string) {
unnamedArgs := cmdAPI.Flag.Args()
if len(unnamedArgs) < 2 {
return newError("service name or request not specified.")
base.Fatalf("service name or request not specified.")
}

service, method := getServiceMethod(unnamedArgs[0])
handler, found := serivceHandlerMap[strings.ToLower(service)]
if !found {
return newError("unknown service: ", service)
base.Fatalf("unknown service: %s", service)
}

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

conn, err := grpc.DialContext(ctx, *serverAddrPtr, grpc.WithInsecure(), grpc.WithBlock())
conn, err := grpc.DialContext(ctx, *apiServerAddrPtr, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
return newError("failed to dial ", *serverAddrPtr).Base(err)
base.Fatalf("failed to dial %s", *apiServerAddrPtr)
}
defer conn.Close()

response, err := handler(ctx, conn, method, unnamedArgs[1])
if err != nil {
return newError("failed to call service ", unnamedArgs[0]).Base(err)
base.Fatalf("failed to call service %s: %s", unnamedArgs[0], err)
}

fmt.Println(response)
return nil
}

func getServiceMethod(s string) (string, string) {
Expand All @@ -103,9 +97,6 @@ func callLogService(ctx context.Context, conn *grpc.ClientConn, method string, r
switch strings.ToLower(method) {
case "restartlogger":
r := &logService.RestartLoggerRequest{}
if err := proto.UnmarshalText(request, r); err != nil {
return "", err
}
resp, err := client.RestartLogger(ctx, r)
if err != nil {
return "", err
Expand Down Expand Up @@ -152,7 +143,3 @@ func callStatsService(ctx context.Context, conn *grpc.ClientConn, method string,
return "", errors.New("Unknown method: " + method)
}
}

func init() {
common.Must(RegisterCommand(&APICommand{}))
}
17 changes: 17 additions & 0 deletions commands/all/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package all

import "v2ray.com/core/commands/base"

// go:generate go run v2ray.com/core/common/errors/errorgen

func init() {
base.RootCommand.Commands = append(
base.RootCommand.Commands,
cmdAPI,
cmdConvert,
cmdLove,
cmdTLS,
cmdUUID,
cmdVerify,
)
}
126 changes: 126 additions & 0 deletions commands/all/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package all

import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"

"google.golang.org/protobuf/proto"
"v2ray.com/core/commands/base"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/infra/conf"
"v2ray.com/core/infra/conf/serial"
)

var cmdConvert = &base.Command{
UsageLine: "{{.Exec}} convert [json file] [json file] ...",
Short: "Convert multiple json config to protobuf",
Long: `
Convert multiple json config to protobuf.
Examples:
{{.Exec}} {{.LongName}} config.json c1.json c2.json <url>.json
`,
}

func init() {
cmdConvert.Run = executeConvert // break init loop
}

func executeConvert(cmd *base.Command, args []string) {
unnamedArgs := cmdConvert.Flag.Args()
if len(unnamedArgs) < 1 {
base.Fatalf("empty config list")
}

conf := &conf.Config{}
for _, arg := range unnamedArgs {
fmt.Fprintf(os.Stderr, "Read config: %s", arg)
r, err := loadArg(arg)
common.Must(err)
c, err := serial.DecodeJSONConfig(r)
if err != nil {
base.Fatalf(err.Error())
}
conf.Override(c, arg)
}

pbConfig, err := conf.Build()
if err != nil {
base.Fatalf(err.Error())
}

bytesConfig, err := proto.Marshal(pbConfig)
if err != nil {
base.Fatalf("failed to marshal proto config: %s", err)
}

if _, err := os.Stdout.Write(bytesConfig); err != nil {
base.Fatalf("failed to write proto config: %s", err)
}
}

// loadArg loads one arg, maybe an remote url, or local file path
func loadArg(arg string) (out io.Reader, err error) {
var data []byte
switch {
case strings.HasPrefix(arg, "http://"), strings.HasPrefix(arg, "https://"):
data, err = FetchHTTPContent(arg)

case arg == "stdin:":
data, err = ioutil.ReadAll(os.Stdin)

default:
data, err = ioutil.ReadFile(arg)
}

if err != nil {
return
}
out = bytes.NewBuffer(data)
return
}

// 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 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
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package control
package all

import "v2ray.com/core/common/errors"

Expand Down
37 changes: 37 additions & 0 deletions commands/all/love.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package all

import (
"bufio"
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"

"v2ray.com/core/commands/base"
"v2ray.com/core/common"
"v2ray.com/core/common/platform"
)

var cmdLove = &base.Command{
UsageLine: "{{.Exec}} lovevictoria",
Short: "", // set Short to "" hides the command
Long: "",
Run: executeLove,
}

func executeLove(cmd *base.Command, args []string) {
const content = "H4sIAAAAAAAC/4SVMaskNwzH+/kUW6izcSthMGrcqLhVk0rdQS5cSMg7Xu4S0vizB8meZd57M3ta2GHX/ukvyZZmY2ZKDMzCzJyY5yOlxKII1omsf+qkBiiC6WhbYsbkjDAfySQsJqD3jtrD0EBM3sBHzG3kUsrglIQREXonpd47kYIi4AHmgI9Wcq2jlJITC6JZJ+v3ECYzBMAHyYm392yuY4zWsjACmHZSh6l3A0JETzGlWZqDsnArpTg62mhJONhOdO90p97V1BAnteoaOcuummtrrtuERQwUiJwP8a4KGKcyxdOCw1spOY+WHueFqmakAIgUSSuhwKNgobxKXSLbtg6r5cFmBiAeF6yCkYycmv+BiCIiW8ScHa3DgxAuZQbRhFNrLTFo96RBmx9jKWWG5nBsjyJzuIkftUblonppZU5t5LzwIks5L1a4lijagQxLokbIYwxfytNDC+XQqrWW9fzAunhqh5/Tg8PuaMw0d/Tcw3iDO81bHfWM/AnutMh2xqSUntMzd3wHDy9iHMQz8bmUZYvqedTJ5GgOnrNt7FIbSlwXE3wDI19n/KA38MsLaP4l89b5F8AV3ESOMIEhIBgezHBc0H6xV9KbaXwMvPcNvIHcC0C7UPZQx4JVTb35/AneSQq+bAYXsBmY7TCRupF2NTdVm/+ch22xa0pvRERKqt1oxj9DUbXzU84Gvj5hc5a81SlAUwMwgEs4T9+7sg9lb9h+908MWiKV8xtWciVTmnB3tivRjNerfXdxpfEBbq2NUvLMM5R9NLuyQg8nXT0PIh1xPd/wrcV49oJ6zbZaPlj2V87IY9T3F2XCOcW2MbZyZd49H+9m81E1N9SxlU+ff/1y+/f3719vf7788+Ugv/ffbMIH7ZNj0dsT4WMHHwLPu/Rp2O75uh99AK+N2xn7ZHq1OK6gczkN+9ngdOl1Qvki5xwSR8vFX6D+9vXA97B/+fr5rz9u/738uP328urP19vfP759e3n9Xs6jamvqlfJ/AAAA//+YAMZjDgkAAA=="
c, err := base64.StdEncoding.DecodeString(content)
common.Must(err)
reader, err := gzip.NewReader(bytes.NewBuffer(c))
common.Must(err)
b := make([]byte, 4096)
nBytes, _ := reader.Read(b)

bb := bytes.NewBuffer(b[:nBytes])
scanner := bufio.NewScanner(bb)
for scanner.Scan() {
s := scanner.Text()
fmt.Print(s + platform.LineSeparator())
}
}
18 changes: 18 additions & 0 deletions commands/all/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package all

import (
"v2ray.com/core/commands/all/tlscmd"
"v2ray.com/core/commands/base"
)

var cmdTLS = &base.Command{
UsageLine: "{{.Exec}} tls",
Short: "TLS tools",
Long: `{{.Exec}} tls provides tools for TLS.
`,

Commands: []*base.Command{
tlscmd.CmdCert,
tlscmd.CmdPing,
},
}
Loading

0 comments on commit 521120d

Please sign in to comment.