convert.go 2.7 KB

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