common.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Package common contains common utilities that are shared among other packages.
  2. // See each sub-package for detail.
  3. package common
  4. import (
  5. "fmt"
  6. "go/build"
  7. "io"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "path/filepath"
  12. "strings"
  13. "time"
  14. "github.com/v2fly/v2ray-core/v5/common/errors"
  15. )
  16. //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
  17. // ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route.
  18. var ErrNoClue = errors.New("not enough information for making a decision")
  19. // Must panics if err is not nil.
  20. func Must(err error) {
  21. if err != nil {
  22. panic(err)
  23. }
  24. }
  25. // Must2 panics if the second parameter is not nil, otherwise returns the first parameter.
  26. func Must2(v interface{}, err error) interface{} {
  27. Must(err)
  28. return v
  29. }
  30. // Error2 returns the err from the 2nd parameter.
  31. func Error2(v interface{}, err error) error {
  32. return err
  33. }
  34. // envFile returns the name of the Go environment configuration file.
  35. // Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166
  36. func envFile() (string, error) {
  37. if file := os.Getenv("GOENV"); file != "" {
  38. if file == "off" {
  39. return "", fmt.Errorf("GOENV=off")
  40. }
  41. return file, nil
  42. }
  43. dir, err := os.UserConfigDir()
  44. if err != nil {
  45. return "", err
  46. }
  47. if dir == "" {
  48. return "", fmt.Errorf("missing user-config dir")
  49. }
  50. return filepath.Join(dir, "go", "env"), nil
  51. }
  52. // GetRuntimeEnv returns the value of runtime environment variable,
  53. // that is set by running following command: `go env -w key=value`.
  54. func GetRuntimeEnv(key string) (string, error) {
  55. file, err := envFile()
  56. if err != nil {
  57. return "", err
  58. }
  59. if file == "" {
  60. return "", fmt.Errorf("missing runtime env file")
  61. }
  62. var data []byte
  63. var runtimeEnv string
  64. data, readErr := os.ReadFile(file)
  65. if readErr != nil {
  66. return "", readErr
  67. }
  68. envStrings := strings.Split(string(data), "\n")
  69. for _, envItem := range envStrings {
  70. envItem = strings.TrimSuffix(envItem, "\r")
  71. envKeyValue := strings.Split(envItem, "=")
  72. if strings.EqualFold(strings.TrimSpace(envKeyValue[0]), key) {
  73. runtimeEnv = strings.TrimSpace(envKeyValue[1])
  74. }
  75. }
  76. return runtimeEnv, nil
  77. }
  78. // GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty.
  79. func GetGOBIN() string {
  80. // The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command`
  81. GOBIN := os.Getenv("GOBIN")
  82. if GOBIN == "" {
  83. var err error
  84. // The one set by user by running `go env -w GOBIN=/path`
  85. GOBIN, err = GetRuntimeEnv("GOBIN")
  86. if err != nil {
  87. // The default one that Golang uses
  88. return filepath.Join(build.Default.GOPATH, "bin")
  89. }
  90. if GOBIN == "" {
  91. return filepath.Join(build.Default.GOPATH, "bin")
  92. }
  93. return GOBIN
  94. }
  95. return GOBIN
  96. }
  97. // GetGOPATH returns GOPATH environment variable as a string. It will NOT be empty.
  98. func GetGOPATH() string {
  99. // The one set by user explicitly by `export GOPATH=/path` or `env GOPATH=/path command`
  100. GOPATH := os.Getenv("GOPATH")
  101. if GOPATH == "" {
  102. var err error
  103. // The one set by user by running `go env -w GOPATH=/path`
  104. GOPATH, err = GetRuntimeEnv("GOPATH")
  105. if err != nil {
  106. // The default one that Golang uses
  107. return build.Default.GOPATH
  108. }
  109. if GOPATH == "" {
  110. return build.Default.GOPATH
  111. }
  112. return GOPATH
  113. }
  114. return GOPATH
  115. }
  116. // FetchHTTPContent dials http(s) for remote content
  117. func FetchHTTPContent(target string) ([]byte, error) {
  118. parsedTarget, err := url.Parse(target)
  119. if err != nil {
  120. return nil, newError("invalid URL: ", target).Base(err)
  121. }
  122. if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
  123. return nil, newError("invalid scheme: ", parsedTarget.Scheme)
  124. }
  125. client := &http.Client{
  126. Timeout: 30 * time.Second,
  127. }
  128. resp, err := client.Do(&http.Request{
  129. Method: "GET",
  130. URL: parsedTarget,
  131. Close: true,
  132. })
  133. if err != nil {
  134. return nil, newError("failed to dial to ", target).Base(err)
  135. }
  136. defer resp.Body.Close()
  137. if resp.StatusCode != http.StatusOK {
  138. return nil, newError("unexpected HTTP status code: ", resp.StatusCode)
  139. }
  140. content, err := io.ReadAll(resp.Body)
  141. if err != nil {
  142. return nil, newError("failed to read HTTP response").Base(err)
  143. }
  144. return content, nil
  145. }