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