Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
385a259
add function to read HTTP_PROXY/HTTPS_PROXY/NO_PROXY environment vari…
peterguy Feb 25, 2026
7651284
use new function to read proxy settings from environment, preferring …
peterguy Feb 25, 2026
d2ca262
use http.Request instead of manually building the request, and use Re…
peterguy Feb 25, 2026
d6b88e1
commit to a buffered reader for the proxy connection so we avoid nast…
peterguy Feb 25, 2026
d477d71
clone the transport TLS config instead of creating a new one, and mak…
peterguy Feb 25, 2026
662ff63
fix some comments
peterguy Feb 25, 2026
569629b
whoops, forgot to commit the io import
peterguy Feb 25, 2026
badd02e
dial the proxy, ensuring http/1.1 instead of http/2 for TLS-enabled p…
peterguy Feb 25, 2026
f3e4f82
add proxy tests
peterguy Feb 25, 2026
115b34b
change name of test to match production method
peterguy Feb 25, 2026
af15d4b
add 10ms delay to test proxy server startup to try to fix ubuntu tests
peterguy Feb 26, 2026
b83906c
go-lint.sh
peterguy Feb 26, 2026
f305cb6
wait for test proxy to startup
peterguy Feb 26, 2026
7977998
go-lint.sh
peterguy Feb 26, 2026
380249f
remove cleanEndpoint because it just obscures the one thing it does: …
peterguy Feb 27, 2026
0deaf78
parse the endpoint into a URL up front, and gather the proxy from the…
peterguy Feb 27, 2026
ac284e8
use the parsed endpoint url and consolidate proxy handling because th…
peterguy Feb 27, 2026
1bf5b79
fix proxyDialAddr and add more tests for it
peterguy Feb 27, 2026
43aa0c4
add EndpointURL to the tests and other places that should use it inst…
peterguy Feb 27, 2026
3536377
use the client to connect to the API instead of http.DefaultClient so…
peterguy Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/src/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Examples:
}

func loginCmd(ctx context.Context, cfg *config, client api.Client, endpointArg string, out io.Writer) error {
endpointArg = cleanEndpoint(endpointArg)
endpointArg = strings.TrimSuffix(endpointArg, "/")

printProblem := func(problem string) {
fmt.Fprintf(out, "❌ Problem: %s\n", problem)
Expand Down
7 changes: 5 additions & 2 deletions cmd/src/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"

Expand Down Expand Up @@ -63,7 +64,8 @@ func TestLogin(t *testing.T) {
defer s.Close()

endpoint := s.URL
out, err := check(t, &config{Endpoint: endpoint, AccessToken: "x"}, endpoint)
u, _ := url.ParseRequestURI(endpoint)
out, err := check(t, &config{Endpoint: endpoint, EndpointURL: u, AccessToken: "x"}, endpoint)
if err != cmderrors.ExitCode1 {
t.Fatal(err)
}
Expand All @@ -82,7 +84,8 @@ func TestLogin(t *testing.T) {
defer s.Close()

endpoint := s.URL
out, err := check(t, &config{Endpoint: endpoint, AccessToken: "x"}, endpoint)
u, _ := url.ParseRequestURI(endpoint)
out, err := check(t, &config{Endpoint: endpoint, EndpointURL: u, AccessToken: "x"}, endpoint)
if err != nil {
t.Fatal(err)
}
Expand Down
61 changes: 44 additions & 17 deletions cmd/src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import (
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
Expand Down Expand Up @@ -107,6 +109,20 @@
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.
Expand All @@ -118,12 +134,13 @@
ProxyURL *url.URL
ProxyPath string
ConfigFilePath string
EndpointURL *url.URL
}

// 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,
Expand All @@ -133,7 +150,8 @@
})
}

// 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 != ""
Expand Down Expand Up @@ -189,9 +207,21 @@
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]
Expand All @@ -205,7 +235,7 @@
return slices.Contains(urlSchemes, scheme)
}

scheme, address := parseEndpoint(cfg.Proxy)
scheme, address := parseProxyEndpoint(cfg.Proxy)

if isURLScheme(scheme) {
endpoint := cfg.Proxy
Expand All @@ -227,11 +257,19 @@
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)
}
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)

Check failure on line 270 in cmd/src/main.go

View workflow job for this annotation

GitHub Actions / go-lint

ST1005: error strings should not be capitalized (staticcheck)
} else {
cfg.ProxyURL = u
}
}

Expand All @@ -242,20 +280,9 @@
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:
Expand Down
68 changes: 66 additions & 2 deletions cmd/src/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@
{
name: "defaults",
want: &config{
Endpoint: "https://sourcegraph.com",
Endpoint: "https://sourcegraph.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "sourcegraph.com",
},
AdditionalHeaders: map[string]string{},
},
},
Expand All @@ -54,7 +58,11 @@
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",
Expand Down Expand Up @@ -94,7 +102,11 @@
},
envProxy: "socks5://other.proxy.com:9999",
want: &config{
Endpoint: "https://example.com",

Check failure on line 105 in cmd/src/main_test.go

View workflow job for this annotation

GitHub Actions / go-lint

File is not properly formatted (goimports)
EndpointURL: &url.URL{
Scheme: "https",
Host: "example.com",
},
AccessToken: "deadbeef",
Proxy: "socks5://other.proxy.com:9999",
ProxyPath: "",
Expand All @@ -117,6 +129,10 @@
envProxy: "socks5://other.proxy.com:9999",
want: &config{
Endpoint: "https://override.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "override.com",
},
AccessToken: "abc",
Proxy: "socks5://other.proxy.com:9999",
ProxyPath: "",
Expand All @@ -132,6 +148,10 @@
envToken: "abc",
want: &config{
Endpoint: "https://sourcegraph.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "sourcegraph.com",
},
AccessToken: "abc",
AdditionalHeaders: map[string]string{},
},
Expand All @@ -141,6 +161,10 @@
envEndpoint: "https://example.com",
want: &config{
Endpoint: "https://example.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "example.com",
},
AccessToken: "",
AdditionalHeaders: map[string]string{},
},
Expand All @@ -150,6 +174,10 @@
envProxy: "https://proxy.com:8080",
want: &config{
Endpoint: "https://sourcegraph.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "sourcegraph.com",
},
AccessToken: "",
Proxy: "https://proxy.com:8080",
ProxyPath: "",
Expand All @@ -167,6 +195,10 @@
envProxy: "https://proxy.com:8080",
want: &config{
Endpoint: "https://example.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "example.com",
},
AccessToken: "abc",
Proxy: "https://proxy.com:8080",
ProxyPath: "",
Expand All @@ -182,6 +214,10 @@
envProxy: "unix://" + socketPath,
want: &config{
Endpoint: "https://sourcegraph.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "sourcegraph.com",
},
Proxy: "unix://" + socketPath,
ProxyPath: socketPath,
ProxyURL: nil,
Expand All @@ -193,6 +229,10 @@
envProxy: socketPath,
want: &config{
Endpoint: "https://sourcegraph.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "sourcegraph.com",
},
Proxy: socketPath,
ProxyPath: socketPath,
ProxyURL: nil,
Expand All @@ -204,6 +244,10 @@
envProxy: "socks://localhost:1080",
want: &config{
Endpoint: "https://sourcegraph.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "sourcegraph.com",
},
Proxy: "socks://localhost:1080",
ProxyPath: "",
ProxyURL: &url.URL{
Expand All @@ -218,6 +262,10 @@
envProxy: "socks5h://localhost:1080",
want: &config{
Endpoint: "https://sourcegraph.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "sourcegraph.com",
},
Proxy: "socks5h://localhost:1080",
ProxyPath: "",
ProxyURL: &url.URL{
Expand All @@ -237,6 +285,10 @@
},
want: &config{
Endpoint: "https://override.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "override.com",
},
AccessToken: "deadbeef",
AdditionalHeaders: map[string]string{},
},
Expand All @@ -248,6 +300,10 @@
envToken: "abc",
want: &config{
Endpoint: "https://override.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "override.com",
},
AccessToken: "abc",
AdditionalHeaders: map[string]string{},
},
Expand All @@ -260,6 +316,10 @@
envFooHeader: "bar",
want: &config{
Endpoint: "https://override.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "override.com",
},
AccessToken: "abc",
AdditionalHeaders: map[string]string{"foo": "bar"},
},
Expand All @@ -272,6 +332,10 @@
envHeaders: "foo:bar\nfoo-bar:bar-baz",
want: &config{
Endpoint: "https://override.com",
EndpointURL: &url.URL{
Scheme: "https",
Host: "override.com",
},
AccessToken: "abc",
AdditionalHeaders: map[string]string{"foo-bar": "bar-baz", "foo": "bar"},
},
Expand Down
12 changes: 6 additions & 6 deletions cmd/src/search_jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func parseColumns(columnsFlag string) []string {
// createSearchJobsClient creates a reusable API client for search jobs commands
func createSearchJobsClient(out *flag.FlagSet, apiFlags *api.Flags) api.Client {
return api.NewClient(api.ClientOpts{
Endpoint: cfg.Endpoint,
EndpointURL: cfg.EndpointURL,
AccessToken: cfg.AccessToken,
Out: out.Output(),
Flags: apiFlags,
Expand Down Expand Up @@ -259,11 +259,11 @@ func init() {
usage := `'src search-jobs' is a tool that manages search jobs on a Sourcegraph instance.

Usage:

src search-jobs command [command options]

The commands are:

cancel cancels a search job by ID
create creates a search job
delete deletes a search job by ID
Expand All @@ -272,11 +272,11 @@ func init() {
logs fetches logs for a search job by ID
restart restarts a search job by ID
results fetches results for a search job by ID

Common options for all commands:
-c Select columns to display (e.g., -c id,query,state,username)
-json Output results in JSON format

Use "src search-jobs [command] -h" for more information about a command.
`

Expand Down
Loading
Loading