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