diff --git a/cmd/params/params.go b/cmd/params/params.go index 22f4f92d..f693e4b9 100644 --- a/cmd/params/params.go +++ b/cmd/params/params.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net/url" "os" "regexp" "strconv" @@ -22,6 +23,7 @@ import ( "github.com/mitchellh/go-homedir" "github.com/spf13/viper" + "golang.org/x/net/http/httpproxy" ) const errMsgTemplate = "invalid url %q. Must be in format" + @@ -220,18 +222,23 @@ func LoadAPIParams(v *viper.Viper) (API, error) { }) } - apiURL := api.BaseURL + apiURLStr := api.BaseURL if u, ok := vipertools.FirstNonEmptyString(v, "api-url", "apiurl", "settings.api_url"); ok { - apiURL = u + apiURLStr = u } // remove endpoint from api base url to support legacy api_url param - apiURL = strings.TrimSuffix(apiURL, "/") - apiURL = strings.TrimSuffix(apiURL, ".bulk") - apiURL = strings.TrimSuffix(apiURL, "/users/current/heartbeats") - apiURL = strings.TrimSuffix(apiURL, "/heartbeats") - apiURL = strings.TrimSuffix(apiURL, "/heartbeat") + apiURLStr = strings.TrimSuffix(apiURLStr, "/") + apiURLStr = strings.TrimSuffix(apiURLStr, ".bulk") + apiURLStr = strings.TrimSuffix(apiURLStr, "/users/current/heartbeats") + apiURLStr = strings.TrimSuffix(apiURLStr, "/heartbeats") + apiURLStr = strings.TrimSuffix(apiURLStr, "/heartbeat") + + apiURL, err := url.Parse(apiURLStr) + if err != nil { + return API{}, fmt.Errorf("invalid api url: %s", err) + } var backoffAt time.Time @@ -257,10 +264,7 @@ func LoadAPIParams(v *viper.Viper) (API, error) { } } - var ( - hostname string - err error - ) + var hostname string hostname, ok = vipertools.FirstNonEmptyString(v, "hostname", "settings.hostname") if !ok { @@ -281,6 +285,18 @@ func LoadAPIParams(v *viper.Viper) (API, error) { return API{}, fmt.Errorf(errMsgTemplate, proxyURL) } + proxyEnv := httpproxy.FromEnvironment() + + proxyEnvURL, err := proxyEnv.ProxyFunc()(apiURL) + if err != nil { + log.Warnf("failed to get proxy url from environment for api url: %s", err) + } + + // try use proxy from environment if no custom proxy is set + if proxyURL == "" && proxyEnvURL != nil { + proxyURL = proxyEnvURL.String() + } + var sslCertFilepath string sslCertFilepath, ok = vipertools.FirstNonEmptyString(v, "ssl-certs-file", "settings.ssl_certs_file") @@ -309,7 +325,7 @@ func LoadAPIParams(v *viper.Viper) (API, error) { ProxyURL: proxyURL, SSLCertFilepath: sslCertFilepath, Timeout: timeout, - URL: apiURL, + URL: apiURL.String(), }, nil } diff --git a/cmd/params/params_test.go b/cmd/params/params_test.go index 8b38fa38..415987a3 100644 --- a/cmd/params/params_test.go +++ b/cmd/params/params_test.go @@ -1846,6 +1846,22 @@ func TestLoad_API_ProxyURL_FlagTakesPrecedence(t *testing.T) { assert.Equal(t, "https://john:secret@example.org:8888", params.ProxyURL) } +func TestLoad_API_ProxyURL_UserDefinedTakesPrecedenceOverEnvironment(t *testing.T) { + v := viper.New() + v.Set("key", "00000000-0000-4000-8000-000000000000") + v.Set("proxy", "https://john:secret@example.org:8888") + + err := os.Setenv("HTTPS_PROXY", "https://papa:secret@company.org:9000") + require.NoError(t, err) + + defer os.Unsetenv("HTTPS_PROXY") + + params, err := paramscmd.LoadAPIParams(v) + require.NoError(t, err) + + assert.Equal(t, "https://john:secret@example.org:8888", params.ProxyURL) +} + func TestLoad_API_ProxyURL_FromConfig(t *testing.T) { v := viper.New() v.Set("key", "00000000-0000-4000-8000-000000000000") @@ -1857,6 +1873,36 @@ func TestLoad_API_ProxyURL_FromConfig(t *testing.T) { assert.Equal(t, "https://john:secret@example.org:8888", params.ProxyURL) } +func TestLoad_API_ProxyURL_FromEnvironment(t *testing.T) { + v := viper.New() + v.Set("key", "00000000-0000-4000-8000-000000000000") + + err := os.Setenv("HTTPS_PROXY", "https://john:secret@example.org:8888") + require.NoError(t, err) + + defer os.Unsetenv("HTTPS_PROXY") + + params, err := paramscmd.LoadAPIParams(v) + require.NoError(t, err) + + assert.Equal(t, "https://john:secret@example.org:8888", params.ProxyURL) +} + +func TestLoad_API_ProxyURL_NoProxyFromEnvironment(t *testing.T) { + v := viper.New() + v.Set("key", "00000000-0000-4000-8000-000000000000") + + err := os.Setenv("NO_PROXY", "https://some.org,https://api.wakatime.com") + require.NoError(t, err) + + defer os.Unsetenv("NO_PROXY") + + params, err := paramscmd.LoadAPIParams(v) + require.NoError(t, err) + + assert.Empty(t, params.ProxyURL) +} + func TestLoad_API_ProxyURL_InvalidFormat(t *testing.T) { v := viper.New() v.Set("key", "00000000-0000-4000-8000-000000000000") diff --git a/go.mod b/go.mod index 533f9102..d561d401 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/yookoala/realpath v1.0.0 go.etcd.io/bbolt v1.3.6 golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e + golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c gopkg.in/ini.v1 v1.66.6 ) @@ -44,7 +45,7 @@ require ( github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.0 // indirect - golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index bc5c5ea1..de70936c 100644 --- a/go.sum +++ b/go.sum @@ -355,7 +355,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= +golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= +golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -415,10 +416,10 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=