github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/main/commands/all/api/shared.go (about)

     1  package api
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"os"
    11  	"reflect"
    12  	"strings"
    13  	"time"
    14  
    15  	"google.golang.org/grpc/credentials/insecure"
    16  	"google.golang.org/protobuf/encoding/protojson"
    17  
    18  	"github.com/xtls/xray-core/common/buf"
    19  	"github.com/xtls/xray-core/main/commands/base"
    20  	"google.golang.org/grpc"
    21  	"google.golang.org/protobuf/proto"
    22  )
    23  
    24  type serviceHandler func(ctx context.Context, conn *grpc.ClientConn, cmd *base.Command, args []string) string
    25  
    26  var (
    27  	apiServerAddrPtr string
    28  	apiTimeout       int
    29  	apiJSON          bool
    30  )
    31  
    32  func setSharedFlags(cmd *base.Command) {
    33  	cmd.Flag.StringVar(&apiServerAddrPtr, "s", "127.0.0.1:8080", "")
    34  	cmd.Flag.StringVar(&apiServerAddrPtr, "server", "127.0.0.1:8080", "")
    35  	cmd.Flag.IntVar(&apiTimeout, "t", 3, "")
    36  	cmd.Flag.IntVar(&apiTimeout, "timeout", 3, "")
    37  	cmd.Flag.BoolVar(&apiJSON, "json", false, "")
    38  }
    39  
    40  func dialAPIServer() (conn *grpc.ClientConn, ctx context.Context, close func()) {
    41  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(apiTimeout)*time.Second)
    42  	conn, err := grpc.DialContext(ctx, apiServerAddrPtr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
    43  	if err != nil {
    44  		base.Fatalf("failed to dial %s", apiServerAddrPtr)
    45  	}
    46  	close = func() {
    47  		cancel()
    48  		conn.Close()
    49  	}
    50  	return
    51  }
    52  
    53  // loadArg loads one arg, maybe an remote url, or local file path
    54  func loadArg(arg string) (out io.Reader, err error) {
    55  	var data []byte
    56  	switch {
    57  	case strings.HasPrefix(arg, "http://"), strings.HasPrefix(arg, "https://"):
    58  		data, err = fetchHTTPContent(arg)
    59  
    60  	case arg == "stdin:":
    61  		data, err = io.ReadAll(os.Stdin)
    62  
    63  	default:
    64  		data, err = os.ReadFile(arg)
    65  	}
    66  
    67  	if err != nil {
    68  		return
    69  	}
    70  	out = bytes.NewBuffer(data)
    71  	return
    72  }
    73  
    74  // fetchHTTPContent dials https for remote content
    75  func fetchHTTPContent(target string) ([]byte, error) {
    76  	parsedTarget, err := url.Parse(target)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
    82  		return nil, fmt.Errorf("invalid scheme: %s", parsedTarget.Scheme)
    83  	}
    84  
    85  	client := &http.Client{
    86  		Timeout: 30 * time.Second,
    87  	}
    88  	resp, err := client.Do(&http.Request{
    89  		Method: "GET",
    90  		URL:    parsedTarget,
    91  		Close:  true,
    92  	})
    93  	if err != nil {
    94  		return nil, fmt.Errorf("failed to dial to %s", target)
    95  	}
    96  	defer resp.Body.Close()
    97  
    98  	if resp.StatusCode != 200 {
    99  		return nil, fmt.Errorf("unexpected HTTP status code: %d", resp.StatusCode)
   100  	}
   101  
   102  	content, err := buf.ReadAllToBytes(resp.Body)
   103  	if err != nil {
   104  		return nil, fmt.Errorf("failed to read HTTP response")
   105  	}
   106  
   107  	return content, nil
   108  }
   109  
   110  func protoToJSONString(m proto.Message, prefix, indent string) (string, error) {
   111  	return strings.TrimSpace(protojson.MarshalOptions{Indent: indent}.Format(m)), nil
   112  }
   113  
   114  func showJSONResponse(m proto.Message) {
   115  	if isNil(m) {
   116  		return
   117  	}
   118  	output, err := protoToJSONString(m, "", "    ")
   119  	if err != nil {
   120  		fmt.Fprintf(os.Stdout, "%v\n", m)
   121  		base.Fatalf("error encode json: %s", err)
   122  	}
   123  	fmt.Println(output)
   124  }
   125  
   126  func isNil(i interface{}) bool {
   127  	vi := reflect.ValueOf(i)
   128  	if vi.Kind() == reflect.Ptr {
   129  		return vi.IsNil()
   130  	}
   131  	return i == nil
   132  }