github.com/eagleql/xray-core@v1.4.4/main/commands/all/api/shared.go (about)

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