mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-12-24 22:49:48 +00:00
165 lines
4.2 KiB
Go
165 lines
4.2 KiB
Go
|
package all
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"google.golang.org/grpc"
|
||
|
"google.golang.org/protobuf/proto"
|
||
|
|
||
|
logService "github.com/xtls/xray-core/v1/app/log/command"
|
||
|
statsService "github.com/xtls/xray-core/v1/app/stats/command"
|
||
|
"github.com/xtls/xray-core/v1/main/commands/base"
|
||
|
)
|
||
|
|
||
|
// cmdAPI calls an API in an Xray process
|
||
|
var cmdAPI = &base.Command{
|
||
|
UsageLine: "{{.Exec}} api [-server 127.0.0.1:8080] <action> <parameter>",
|
||
|
Short: "Call an API in a Xray process",
|
||
|
Long: `
|
||
|
Call an API in a Xray process, API calls in this command have a timeout to the server of 3 seconds.
|
||
|
|
||
|
The following methods are currently supported:
|
||
|
|
||
|
LoggerService.RestartLogger
|
||
|
StatsService.GetStats
|
||
|
StatsService.QueryStats
|
||
|
|
||
|
Examples:
|
||
|
|
||
|
{{.Exec}} api --server=127.0.0.1:8080 LoggerService.RestartLogger ''
|
||
|
{{.Exec}} api --server=127.0.0.1:8080 StatsService.QueryStats 'pattern: "" reset: false'
|
||
|
{{.Exec}} api --server=127.0.0.1:8080 StatsService.GetStats 'name: "inbound>>>statin>>>traffic>>>downlink" reset: false'
|
||
|
{{.Exec}} api --server=127.0.0.1:8080 StatsService.GetSysStats ''
|
||
|
`,
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
cmdAPI.Run = executeAPI // break init loop
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
base.Fatalf("service name or request not specified.")
|
||
|
}
|
||
|
|
||
|
service, method := getServiceMethod(unnamedArgs[0])
|
||
|
handler, found := serivceHandlerMap[strings.ToLower(service)]
|
||
|
if !found {
|
||
|
base.Fatalf("unknown service: %s", service)
|
||
|
}
|
||
|
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||
|
defer cancel()
|
||
|
|
||
|
conn, err := grpc.DialContext(ctx, *apiServerAddrPtr, grpc.WithInsecure(), grpc.WithBlock())
|
||
|
if err != nil {
|
||
|
base.Fatalf("failed to dial %s", *apiServerAddrPtr)
|
||
|
}
|
||
|
defer conn.Close()
|
||
|
|
||
|
response, err := handler(ctx, conn, method, unnamedArgs[1])
|
||
|
if err != nil {
|
||
|
base.Fatalf("failed to call service %s", unnamedArgs[0])
|
||
|
}
|
||
|
|
||
|
fmt.Println(response)
|
||
|
}
|
||
|
|
||
|
func getServiceMethod(s string) (string, string) {
|
||
|
ss := strings.Split(s, ".")
|
||
|
service := ss[0]
|
||
|
var method string
|
||
|
if len(ss) > 1 {
|
||
|
method = ss[1]
|
||
|
}
|
||
|
return service, method
|
||
|
}
|
||
|
|
||
|
type serviceHandler func(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error)
|
||
|
|
||
|
var serivceHandlerMap = map[string]serviceHandler{
|
||
|
"statsservice": callStatsService,
|
||
|
"loggerservice": callLogService,
|
||
|
}
|
||
|
|
||
|
func callLogService(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error) {
|
||
|
client := logService.NewLoggerServiceClient(conn)
|
||
|
|
||
|
switch strings.ToLower(method) {
|
||
|
case "restartlogger":
|
||
|
r := &logService.RestartLoggerRequest{}
|
||
|
if err := proto.Unmarshal([]byte(request), r); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
resp, err := client.RestartLogger(ctx, r)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
m, err := proto.Marshal(resp)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return string(m), nil
|
||
|
default:
|
||
|
return "", errors.New("Unknown method: " + method)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func callStatsService(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error) {
|
||
|
client := statsService.NewStatsServiceClient(conn)
|
||
|
|
||
|
switch strings.ToLower(method) {
|
||
|
case "getstats":
|
||
|
r := &statsService.GetStatsRequest{}
|
||
|
if err := proto.Unmarshal([]byte(request), r); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
resp, err := client.GetStats(ctx, r)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
m, err := proto.Marshal(resp)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return string(m), nil
|
||
|
case "querystats":
|
||
|
r := &statsService.QueryStatsRequest{}
|
||
|
if err := proto.Unmarshal([]byte(request), r); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
resp, err := client.QueryStats(ctx, r)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
m, err := proto.Marshal(resp)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return string(m), nil
|
||
|
case "getsysstats":
|
||
|
// SysStatsRequest is an empty message
|
||
|
r := &statsService.SysStatsRequest{}
|
||
|
resp, err := client.GetSysStats(ctx, r)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
m, err := proto.Marshal(resp)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return string(m), nil
|
||
|
default:
|
||
|
return "", errors.New("Unknown method: " + method)
|
||
|
}
|
||
|
}
|