-
Notifications
You must be signed in to change notification settings - Fork 69
Add support for HTTP_PROXY, HTTPS_PROXY, and NO_PROXY #1260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
385a259
7651284
d2ca262
d6b88e1
d477d71
662ff63
569629b
badd02e
f3e4f82
115b34b
af15d4b
b83906c
f305cb6
7977998
380249f
0deaf78
ac284e8
1bf5b79
43aa0c4
3536377
419f6f7
76bfc55
78eadd2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,9 +3,11 @@ package main | |
| import ( | ||
| "encoding/json" | ||
| "flag" | ||
| "fmt" | ||
| "io" | ||
| "log" | ||
| "net" | ||
| "net/http" | ||
| "net/url" | ||
| "os" | ||
| "path/filepath" | ||
|
|
@@ -107,6 +109,20 @@ func normalizeDashHelp(args []string) []string { | |
| return args | ||
| } | ||
|
|
||
| func parseEndpoint(endpoint string) (*url.URL, error) { | ||
| u, err := url.ParseRequestURI(strings.TrimSuffix(endpoint, "/")) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if !(u.Scheme == "http" || u.Scheme == "https") { | ||
| return nil, errors.Newf("Invalid scheme %s. Require http or https", u.Scheme) | ||
| } | ||
| if u.Host == "" { | ||
| return nil, errors.Newf("Empty host") | ||
| } | ||
| return u, nil | ||
| } | ||
|
|
||
| var cfg *config | ||
|
|
||
| // config represents the config format. | ||
|
|
@@ -118,12 +134,13 @@ type config struct { | |
| ProxyURL *url.URL | ||
| ProxyPath string | ||
| ConfigFilePath string | ||
| EndpointURL *url.URL | ||
|
Comment on lines
134
to
+137
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. minor: but these fields should probably all not be exported. Looks like we don't want to marshal them / they are internally read/set. |
||
| } | ||
|
|
||
| // apiClient returns an api.Client built from the configuration. | ||
| func (c *config) apiClient(flags *api.Flags, out io.Writer) api.Client { | ||
| return api.NewClient(api.ClientOpts{ | ||
| Endpoint: c.Endpoint, | ||
| EndpointURL: c.EndpointURL, | ||
| AccessToken: c.AccessToken, | ||
| AdditionalHeaders: c.AdditionalHeaders, | ||
| Flags: flags, | ||
|
|
@@ -133,7 +150,8 @@ func (c *config) apiClient(flags *api.Flags, out io.Writer) api.Client { | |
| }) | ||
| } | ||
|
|
||
| // readConfig reads the config file from the given path. | ||
| // readConfig reads the config from the standard config file, the (deprecated) user-specified config file, | ||
| // the environment variables, and the (deprecated) command-line flags. | ||
| func readConfig() (*config, error) { | ||
| cfgFile := *configPath | ||
| userSpecified := *configPath != "" | ||
|
|
@@ -189,9 +207,21 @@ func readConfig() (*config, error) { | |
| cfg.Proxy = envProxy | ||
| } | ||
|
|
||
| // Lastly, apply endpoint flag if set | ||
| if endpoint != nil && *endpoint != "" { | ||
| cfg.Endpoint = *endpoint | ||
| } | ||
|
|
||
| if endpointURL, err := parseEndpoint(cfg.Endpoint); err != nil { | ||
| return nil, errors.Newf("invalid endpoint: %s", cfg.Endpoint) | ||
| } else { | ||
| cfg.EndpointURL = endpointURL | ||
| cfg.Endpoint = endpointURL.String() | ||
| } | ||
|
|
||
| if cfg.Proxy != "" { | ||
|
|
||
| parseEndpoint := func(endpoint string) (scheme string, address string) { | ||
| parseProxyEndpoint := func(endpoint string) (scheme string, address string) { | ||
| parts := strings.SplitN(endpoint, "://", 2) | ||
| if len(parts) == 2 { | ||
| return parts[0], parts[1] | ||
|
|
@@ -205,7 +235,7 @@ func readConfig() (*config, error) { | |
| return slices.Contains(urlSchemes, scheme) | ||
| } | ||
|
|
||
| scheme, address := parseEndpoint(cfg.Proxy) | ||
| scheme, address := parseProxyEndpoint(cfg.Proxy) | ||
|
|
||
| if isURLScheme(scheme) { | ||
| endpoint := cfg.Proxy | ||
|
|
@@ -227,11 +257,19 @@ func readConfig() (*config, error) { | |
| return nil, errors.Newf("Invalid proxy configuration: %w", err) | ||
| } | ||
| if !isValidUDS { | ||
| return nil, errors.Newf("invalid proxy socket: %s", path) | ||
| return nil, errors.Newf("Invalid proxy socket: %s", path) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you changed this is some other places as well. go convention is lowercase on error messages since they get combined. eg |
||
| } | ||
| cfg.ProxyPath = path | ||
| } else { | ||
| return nil, errors.Newf("invalid proxy endpoint: %s", cfg.Proxy) | ||
| return nil, errors.Newf("Invalid proxy endpoint: %s", cfg.Proxy) | ||
| } | ||
| } else { | ||
| // no SRC_PROXY; check for the standard proxy env variables HTTP_PROXY, HTTPS_PROXY, and NO_PROXY | ||
| if u, err := http.ProxyFromEnvironment(&http.Request{URL: cfg.EndpointURL}); err != nil { | ||
| // when there's an error, the value for the env variable is not a legit URL | ||
| return nil, fmt.Errorf("invalid HTTP_PROXY or HTTPS_PROXY value: %w", err) | ||
| } else { | ||
| cfg.ProxyURL = u | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -242,20 +280,9 @@ func readConfig() (*config, error) { | |
| return nil, errConfigAuthorizationConflict | ||
| } | ||
|
|
||
| // Lastly, apply endpoint flag if set | ||
| if endpoint != nil && *endpoint != "" { | ||
| cfg.Endpoint = *endpoint | ||
| } | ||
|
|
||
| cfg.Endpoint = cleanEndpoint(cfg.Endpoint) | ||
|
|
||
| return &cfg, nil | ||
| } | ||
|
|
||
| func cleanEndpoint(urlStr string) string { | ||
| return strings.TrimSuffix(urlStr, "/") | ||
| } | ||
|
|
||
| // isValidUnixSocket checks if the given path is a valid Unix socket. | ||
| // | ||
| // Parameters: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,7 +42,11 @@ func TestReadConfig(t *testing.T) { | |
| { | ||
| name: "defaults", | ||
| want: &config{ | ||
| Endpoint: "https://sourcegraph.com", | ||
| Endpoint: "https://sourcegraph.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "sourcegraph.com", | ||
| }, | ||
| AdditionalHeaders: map[string]string{}, | ||
| }, | ||
| }, | ||
|
|
@@ -54,7 +58,11 @@ func TestReadConfig(t *testing.T) { | |
| Proxy: "https://proxy.com:8080", | ||
| }, | ||
| want: &config{ | ||
| Endpoint: "https://example.com", | ||
| Endpoint: "https://example.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "example.com", | ||
| }, | ||
| AccessToken: "deadbeef", | ||
| AdditionalHeaders: map[string]string{}, | ||
| Proxy: "https://proxy.com:8080", | ||
|
|
@@ -94,7 +102,11 @@ func TestReadConfig(t *testing.T) { | |
| }, | ||
| envProxy: "socks5://other.proxy.com:9999", | ||
| want: &config{ | ||
| Endpoint: "https://example.com", | ||
| Endpoint: "https://example.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "example.com", | ||
| }, | ||
| AccessToken: "deadbeef", | ||
| Proxy: "socks5://other.proxy.com:9999", | ||
| ProxyPath: "", | ||
|
|
@@ -116,7 +128,11 @@ func TestReadConfig(t *testing.T) { | |
| envEndpoint: "https://override.com", | ||
| envProxy: "socks5://other.proxy.com:9999", | ||
| want: &config{ | ||
| Endpoint: "https://override.com", | ||
| Endpoint: "https://override.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "override.com", | ||
| }, | ||
| AccessToken: "abc", | ||
| Proxy: "socks5://other.proxy.com:9999", | ||
| ProxyPath: "", | ||
|
|
@@ -131,7 +147,11 @@ func TestReadConfig(t *testing.T) { | |
| name: "no config file, token from environment", | ||
| envToken: "abc", | ||
| want: &config{ | ||
| Endpoint: "https://sourcegraph.com", | ||
| Endpoint: "https://sourcegraph.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "sourcegraph.com", | ||
| }, | ||
| AccessToken: "abc", | ||
| AdditionalHeaders: map[string]string{}, | ||
| }, | ||
|
|
@@ -140,7 +160,11 @@ func TestReadConfig(t *testing.T) { | |
| name: "no config file, endpoint from environment", | ||
| envEndpoint: "https://example.com", | ||
| want: &config{ | ||
| Endpoint: "https://example.com", | ||
| Endpoint: "https://example.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "example.com", | ||
| }, | ||
| AccessToken: "", | ||
| AdditionalHeaders: map[string]string{}, | ||
| }, | ||
|
|
@@ -149,7 +173,11 @@ func TestReadConfig(t *testing.T) { | |
| name: "no config file, proxy from environment", | ||
| envProxy: "https://proxy.com:8080", | ||
| want: &config{ | ||
| Endpoint: "https://sourcegraph.com", | ||
| Endpoint: "https://sourcegraph.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "sourcegraph.com", | ||
| }, | ||
| AccessToken: "", | ||
| Proxy: "https://proxy.com:8080", | ||
| ProxyPath: "", | ||
|
|
@@ -166,7 +194,11 @@ func TestReadConfig(t *testing.T) { | |
| envToken: "abc", | ||
| envProxy: "https://proxy.com:8080", | ||
| want: &config{ | ||
| Endpoint: "https://example.com", | ||
| Endpoint: "https://example.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "example.com", | ||
| }, | ||
| AccessToken: "abc", | ||
| Proxy: "https://proxy.com:8080", | ||
| ProxyPath: "", | ||
|
|
@@ -181,7 +213,11 @@ func TestReadConfig(t *testing.T) { | |
| name: "UNIX Domain Socket proxy using scheme and absolute path", | ||
| envProxy: "unix://" + socketPath, | ||
| want: &config{ | ||
| Endpoint: "https://sourcegraph.com", | ||
| Endpoint: "https://sourcegraph.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "sourcegraph.com", | ||
| }, | ||
| Proxy: "unix://" + socketPath, | ||
| ProxyPath: socketPath, | ||
| ProxyURL: nil, | ||
|
|
@@ -192,7 +228,11 @@ func TestReadConfig(t *testing.T) { | |
| name: "UNIX Domain Socket proxy with absolute path", | ||
| envProxy: socketPath, | ||
| want: &config{ | ||
| Endpoint: "https://sourcegraph.com", | ||
| Endpoint: "https://sourcegraph.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "sourcegraph.com", | ||
| }, | ||
| Proxy: socketPath, | ||
| ProxyPath: socketPath, | ||
| ProxyURL: nil, | ||
|
|
@@ -203,7 +243,11 @@ func TestReadConfig(t *testing.T) { | |
| name: "socks --> socks5", | ||
| envProxy: "socks://localhost:1080", | ||
| want: &config{ | ||
| Endpoint: "https://sourcegraph.com", | ||
| Endpoint: "https://sourcegraph.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "sourcegraph.com", | ||
| }, | ||
| Proxy: "socks://localhost:1080", | ||
| ProxyPath: "", | ||
| ProxyURL: &url.URL{ | ||
|
|
@@ -217,7 +261,11 @@ func TestReadConfig(t *testing.T) { | |
| name: "socks5h", | ||
| envProxy: "socks5h://localhost:1080", | ||
| want: &config{ | ||
| Endpoint: "https://sourcegraph.com", | ||
| Endpoint: "https://sourcegraph.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "sourcegraph.com", | ||
| }, | ||
| Proxy: "socks5h://localhost:1080", | ||
| ProxyPath: "", | ||
| ProxyURL: &url.URL{ | ||
|
|
@@ -236,7 +284,11 @@ func TestReadConfig(t *testing.T) { | |
| AdditionalHeaders: map[string]string{}, | ||
| }, | ||
| want: &config{ | ||
| Endpoint: "https://override.com", | ||
| Endpoint: "https://override.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "override.com", | ||
| }, | ||
| AccessToken: "deadbeef", | ||
| AdditionalHeaders: map[string]string{}, | ||
| }, | ||
|
|
@@ -247,7 +299,11 @@ func TestReadConfig(t *testing.T) { | |
| envEndpoint: "https://example.com", | ||
| envToken: "abc", | ||
| want: &config{ | ||
| Endpoint: "https://override.com", | ||
| Endpoint: "https://override.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "override.com", | ||
| }, | ||
| AccessToken: "abc", | ||
| AdditionalHeaders: map[string]string{}, | ||
| }, | ||
|
|
@@ -259,7 +315,11 @@ func TestReadConfig(t *testing.T) { | |
| envToken: "abc", | ||
| envFooHeader: "bar", | ||
| want: &config{ | ||
| Endpoint: "https://override.com", | ||
| Endpoint: "https://override.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "override.com", | ||
| }, | ||
| AccessToken: "abc", | ||
| AdditionalHeaders: map[string]string{"foo": "bar"}, | ||
| }, | ||
|
|
@@ -271,7 +331,11 @@ func TestReadConfig(t *testing.T) { | |
| envToken: "abc", | ||
| envHeaders: "foo:bar\nfoo-bar:bar-baz", | ||
| want: &config{ | ||
| Endpoint: "https://override.com", | ||
| Endpoint: "https://override.com", | ||
| EndpointURL: &url.URL{ | ||
| Scheme: "https", | ||
| Host: "override.com", | ||
| }, | ||
|
Comment on lines
+335
to
+338
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it worth updating all these test cases for this? I'd consider just not comparing this field in the comparsion in the test. |
||
| AccessToken: "abc", | ||
| AdditionalHeaders: map[string]string{"foo-bar": "bar-baz", "foo": "bar"}, | ||
| }, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Re: line +130]
I think you should marshal into a struct with just the json fields here. Then from your readConfig function return something which just contains the fields you want to read. Then you don't have both Endpoint and EndpointURL in this struct which is a bit weird.
See this comment inline on Graphite.