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

     1  package all
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/url"
    10  	"os"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/eagleql/xray-core/common"
    15  	"github.com/eagleql/xray-core/common/buf"
    16  	"github.com/eagleql/xray-core/infra/conf"
    17  	"github.com/eagleql/xray-core/infra/conf/serial"
    18  	"github.com/eagleql/xray-core/main/commands/base"
    19  	"google.golang.org/protobuf/proto"
    20  )
    21  
    22  var cmdConvert = &base.Command{
    23  	UsageLine: "{{.Exec}} convert [json file] [json file] ...",
    24  	Short:     "Convert multiple json config to protobuf",
    25  	Long: `
    26  Convert multiple json config to protobuf.
    27  
    28  Examples:
    29  
    30      {{.Exec}} convert config.json c1.json c2.json <url>.json
    31  	`,
    32  }
    33  
    34  func init() {
    35  	cmdConvert.Run = executeConvert // break init loop
    36  }
    37  
    38  func executeConvert(cmd *base.Command, args []string) {
    39  	unnamedArgs := cmdConvert.Flag.Args()
    40  	if len(unnamedArgs) < 1 {
    41  		base.Fatalf("empty config list")
    42  	}
    43  
    44  	conf := &conf.Config{}
    45  	for _, arg := range unnamedArgs {
    46  		fmt.Fprintf(os.Stderr, "Read config: %s", arg)
    47  		r, err := loadArg(arg)
    48  		common.Must(err)
    49  		c, err := serial.DecodeJSONConfig(r)
    50  		if err != nil {
    51  			base.Fatalf(err.Error())
    52  		}
    53  		conf.Override(c, arg)
    54  	}
    55  
    56  	pbConfig, err := conf.Build()
    57  	if err != nil {
    58  		base.Fatalf(err.Error())
    59  	}
    60  
    61  	bytesConfig, err := proto.Marshal(pbConfig)
    62  	if err != nil {
    63  		base.Fatalf("failed to marshal proto config: %s", err)
    64  	}
    65  
    66  	if _, err := os.Stdout.Write(bytesConfig); err != nil {
    67  		base.Fatalf("failed to write proto config: %s", err)
    68  	}
    69  }
    70  
    71  // loadArg loads one arg, maybe an remote url, or local file path
    72  func loadArg(arg string) (out io.Reader, err error) {
    73  	var data []byte
    74  	switch {
    75  	case strings.HasPrefix(arg, "http://"), strings.HasPrefix(arg, "https://"):
    76  		data, err = FetchHTTPContent(arg)
    77  
    78  	case arg == "stdin:":
    79  		data, err = ioutil.ReadAll(os.Stdin)
    80  
    81  	default:
    82  		data, err = ioutil.ReadFile(arg)
    83  	}
    84  
    85  	if err != nil {
    86  		return
    87  	}
    88  	out = bytes.NewBuffer(data)
    89  	return
    90  }
    91  
    92  // FetchHTTPContent dials https for remote content
    93  func FetchHTTPContent(target string) ([]byte, error) {
    94  	parsedTarget, err := url.Parse(target)
    95  	if err != nil {
    96  		return nil, newError("invalid URL: ", target).Base(err)
    97  	}
    98  
    99  	if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
   100  		return nil, newError("invalid scheme: ", parsedTarget.Scheme)
   101  	}
   102  
   103  	client := &http.Client{
   104  		Timeout: 30 * time.Second,
   105  	}
   106  	resp, err := client.Do(&http.Request{
   107  		Method: "GET",
   108  		URL:    parsedTarget,
   109  		Close:  true,
   110  	})
   111  	if err != nil {
   112  		return nil, newError("failed to dial to ", target).Base(err)
   113  	}
   114  	defer resp.Body.Close()
   115  
   116  	if resp.StatusCode != 200 {
   117  		return nil, newError("unexpected HTTP status code: ", resp.StatusCode)
   118  	}
   119  
   120  	content, err := buf.ReadAllToBytes(resp.Body)
   121  	if err != nil {
   122  		return nil, newError("failed to read HTTP response").Base(err)
   123  	}
   124  
   125  	return content, nil
   126  }