Skip to content
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

Add support for HTTP_PROXY #738

Merged
merged 17 commits into from
Apr 11, 2023
9 changes: 8 additions & 1 deletion internal/cmd/artifacts/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/fpath"
"github.com/saucelabs/saucectl/internal/http"
"github.com/saucelabs/saucectl/internal/segment"
"github.com/saucelabs/saucectl/internal/usage"
"github.com/schollz/progressbar/v3"
Expand All @@ -33,7 +34,12 @@ func DownloadCommand() *cobra.Command {

return nil
},
PreRun: func(cmd *cobra.Command, args []string) {
PreRunE: func(cmd *cobra.Command, args []string) error {
err := http.CheckProxy()
if err != nil {
return fmt.Errorf("invalid HTTP_PROXY value")
}

tracker := segment.DefaultTracker

go func() {
Expand All @@ -43,6 +49,7 @@ func DownloadCommand() *cobra.Command {
)
_ = tracker.Close()
}()
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
jobID := args[0]
Expand Down
9 changes: 8 additions & 1 deletion internal/cmd/artifacts/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/jedib0t/go-pretty/v6/text"
"github.com/saucelabs/saucectl/internal/artifacts"
cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/http"
"github.com/saucelabs/saucectl/internal/segment"
"github.com/saucelabs/saucectl/internal/usage"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -72,7 +73,12 @@ func ListCommand() *cobra.Command {

return nil
},
PreRun: func(cmd *cobra.Command, args []string) {
PreRunE: func(cmd *cobra.Command, args []string) error {
err := http.CheckProxy()
if err != nil {
return fmt.Errorf("invalid HTTP_PROXY value")
}

tracker := segment.DefaultTracker

go func() {
Expand All @@ -82,6 +88,7 @@ func ListCommand() *cobra.Command {
)
_ = tracker.Close()
}()
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return list(args[0], out)
Expand Down
9 changes: 8 additions & 1 deletion internal/cmd/artifacts/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/http"
"github.com/saucelabs/saucectl/internal/segment"
"github.com/saucelabs/saucectl/internal/usage"
"github.com/schollz/progressbar/v3"
Expand All @@ -31,7 +32,12 @@ func UploadCommand() *cobra.Command {

return nil
},
PreRun: func(cmd *cobra.Command, args []string) {
PreRunE: func(cmd *cobra.Command, args []string) error {
err := http.CheckProxy()
if err != nil {
return fmt.Errorf("invalid HTTP_PROXY value")
}

tracker := segment.DefaultTracker

go func() {
Expand All @@ -41,6 +47,7 @@ func UploadCommand() *cobra.Command {
)
_ = tracker.Close()
}()
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
jobID := args[0]
Expand Down
9 changes: 8 additions & 1 deletion internal/cmd/imagerunner/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
szip "github.com/saucelabs/saucectl/internal/archive/zip"
cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/files"
"github.com/saucelabs/saucectl/internal/http"
"github.com/saucelabs/saucectl/internal/imagerunner"
"github.com/saucelabs/saucectl/internal/segment"
"github.com/saucelabs/saucectl/internal/usage"
Expand Down Expand Up @@ -91,7 +92,12 @@ func downloadCommand() *cobra.Command {

return nil
},
PreRun: func(cmd *cobra.Command, args []string) {
PreRunE: func(cmd *cobra.Command, args []string) error {
err := http.CheckProxy()
if err != nil {
return fmt.Errorf("invalid HTTP_PROXY value")
}

tracker := segment.DefaultTracker

go func() {
Expand All @@ -101,6 +107,7 @@ func downloadCommand() *cobra.Command {
)
_ = tracker.Close()
}()
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
ID := args[0]
Expand Down
9 changes: 8 additions & 1 deletion internal/cmd/imagerunner/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/http"
imgrunner "github.com/saucelabs/saucectl/internal/imagerunner"
"github.com/saucelabs/saucectl/internal/segment"
"github.com/saucelabs/saucectl/internal/usage"
Expand All @@ -17,7 +18,12 @@ func LogsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "logs <runID>",
Short: "Fetch the logs for an imagerunner run",
PreRun: func(cmd *cobra.Command, args []string) {
PreRunE: func(cmd *cobra.Command, args []string) error {
err := http.CheckProxy()
if err != nil {
return fmt.Errorf("invalid HTTP_PROXY value")
}

tracker := segment.DefaultTracker

go func() {
Expand All @@ -27,6 +33,7 @@ func LogsCommand() *cobra.Command {
)
_ = tracker.Close()
}()
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return exec(args[0])
Expand Down
12 changes: 12 additions & 0 deletions internal/cmd/ini/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/saucelabs/saucectl/internal/cypress"
"github.com/saucelabs/saucectl/internal/espresso"
"github.com/saucelabs/saucectl/internal/flags"
"github.com/saucelabs/saucectl/internal/http"
"github.com/saucelabs/saucectl/internal/imagerunner"
"github.com/saucelabs/saucectl/internal/msg"
"github.com/saucelabs/saucectl/internal/playwright"
Expand Down Expand Up @@ -74,6 +75,9 @@ func Command() *cobra.Command {
Short: initShort,
Long: initLong,
Example: initExample,
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun()
},
Run: func(cmd *cobra.Command, args []string) {
tracker := segment.DefaultTracker

Expand Down Expand Up @@ -109,6 +113,14 @@ func Command() *cobra.Command {
return cmd
}

func preRun() error {
err := http.CheckProxy()
if err != nil {
return fmt.Errorf("invalid HTTP_PROXY value")
}
return nil
}

// Run runs the command
func Run(cmd *cobra.Command, initCfg *initConfig) error {
if cmd.Flags().Changed("framework") {
Expand Down
5 changes: 5 additions & 0 deletions internal/cmd/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ func Command() *cobra.Command {

// preRun is a pre-run step that is executed before the main 'run` step. All shared dependencies are initialized here.
func preRun() error {
err := http.CheckProxy()
if err != nil {
return fmt.Errorf("invalid HTTP_PROXY value")
}

println("Running version", version.Version)
checkForUpdates()
go awaitGlobalTimeout()
Expand Down
11 changes: 7 additions & 4 deletions internal/http/apitester.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ type PublishedTest struct {
// NewAPITester a new instance of APITester.
func NewAPITester(url string, username string, accessKey string, timeout time.Duration) APITester {
return APITester{
HTTPClient: &http.Client{Timeout: timeout},
URL: url,
Username: username,
AccessKey: accessKey,
HTTPClient: &http.Client{
Timeout: timeout,
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
},
URL: url,
Username: username,
AccessKey: accessKey,
}
}

Expand Down
14 changes: 10 additions & 4 deletions internal/http/appstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ type AppStore struct {
// NewAppStore returns an implementation for AppStore
func NewAppStore(url, username, accessKey string, timeout time.Duration) *AppStore {
return &AppStore{
HTTPClient: &http.Client{Timeout: timeout},
URL: url,
Username: username,
AccessKey: accessKey,
HTTPClient: &http.Client{
Timeout: timeout,
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
},
URL: url,
Username: username,
AccessKey: accessKey,
}
}

Expand Down Expand Up @@ -128,6 +131,9 @@ func (s *AppStore) UploadStream(filename, description string, reader io.Reader)
req.SetBasicAuth(s.Username, s.AccessKey)

resp, err := s.HTTPClient.Do(req)
if err != nil {
return storage.Item{}, err
}

switch resp.StatusCode {
case 200, 201:
Expand Down
5 changes: 4 additions & 1 deletion internal/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import (
// NewRetryableClient returns a new pre-configured instance of retryablehttp.Client.
func NewRetryableClient(timeout time.Duration) *retryablehttp.Client {
return &retryablehttp.Client{
HTTPClient: &http.Client{Timeout: timeout},
HTTPClient: &http.Client{
Timeout: timeout,
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
},
RetryWaitMin: 1 * time.Second,
RetryWaitMax: 30 * time.Second,
RetryMax: 3,
Expand Down
7 changes: 5 additions & 2 deletions internal/http/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import (

// DefaultGitHub is a preconfigured instance of GitHub.
var DefaultGitHub = GitHub{
HTTPClient: &http.Client{Timeout: 2 * time.Second},
URL: "https://api.github.com",
HTTPClient: &http.Client{
Timeout: 4 * time.Second,
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
},
URL: "https://api.github.com",
}

// GitHub represents the GitHub HTTP API client.
Expand Down
5 changes: 4 additions & 1 deletion internal/http/insightsservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ var LaunchOptions = map[config.LaunchOrder]string{

func NewInsightsService(url string, creds iam.Credentials, timeout time.Duration) InsightsService {
return InsightsService{
HTTPClient: &http.Client{Timeout: timeout},
HTTPClient: &http.Client{
Timeout: timeout,
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
},
URL: url,
Credentials: creds,
}
Expand Down
86 changes: 86 additions & 0 deletions internal/http/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package http

import (
"fmt"
"net/url"
"os"
"strings"

"github.com/fatih/color"
"github.com/rs/zerolog/log"
)

// Note: The verification logic is borrowed from golang.org/x/net/http/httpproxy.
// Those useful functions are not exposed, but are required to allow us to warn
// the user upfront.
func getEnvAny(names ...string) string {
for _, n := range names {
if val := os.Getenv(n); val != "" {
return val
}
}
return ""
}

func parseProxy(proxy string) (*url.URL, error) {
if proxy == "" {
return nil, nil
}

proxyURL, err := url.Parse(proxy)
if err != nil ||
(proxyURL.Scheme != "http" &&
proxyURL.Scheme != "https" &&
proxyURL.Scheme != "socks5") {
// proxy was bogus. Try prepending "http://" to it and
// see if that parses correctly. If not, we fall
// through and complain about the original one.
if proxyURL, err := url.Parse("http://" + proxy); err == nil {
return proxyURL, nil
}
}
if err != nil {
return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
}
return proxyURL, nil
}

func doCheckProxy(scheme string) error {
proxyScheme := fmt.Sprintf("%s_proxy", scheme)
rawProxyURL := getEnvAny(strings.ToUpper(proxyScheme), strings.ToLower(proxyScheme))
proxyURL, err := parseProxy(rawProxyURL)

if err != nil {
color.Red("\nA proxy has been set, but its url is invalid !\n\n")
fmt.Printf("%s: %s", rawProxyURL, err)
return fmt.Errorf("invalid %s value", strings.ToUpper(proxyScheme))
}
if proxyURL == nil {
return nil
}

// Hide login/password
if proxyURL.User != nil {
pass, hasPass := proxyURL.User.Password()
if hasPass {
rawProxyURL = strings.Replace(rawProxyURL, pass, "****", -1)
}
}
log.Info().Msgf(fmt.Sprintf("Using %s proxy: %s", strings.ToUpper(scheme), rawProxyURL))
return nil
}

// CheckProxy checks that the HTTP_PROXY is valid if it exists.
func CheckProxy() error {
var errs []error
if err := doCheckProxy("http"); err != nil {
errs = append(errs, err)
}
if err := doCheckProxy("https"); err != nil {
errs = append(errs, err)
}
if len(errs) != 0 {
return fmt.Errorf("proxy setup has %d error(s)", len(errs))
}
return nil
}
5 changes: 4 additions & 1 deletion internal/http/testcomposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ type runner struct {

func NewTestComposer(url string, creds iam.Credentials, timeout time.Duration) TestComposer {
return TestComposer{
HTTPClient: &http.Client{Timeout: timeout},
HTTPClient: &http.Client{
Timeout: timeout,
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
},
URL: url,
Credentials: creds,
}
Expand Down
5 changes: 4 additions & 1 deletion internal/http/userservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ type UserService struct {

func NewUserService(url string, creds iam.Credentials, timeout time.Duration) UserService {
return UserService{
HTTPClient: &http.Client{Timeout: timeout},
HTTPClient: &http.Client{
Timeout: timeout,
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
},
URL: url,
Credentials: creds,
}
Expand Down
3 changes: 2 additions & 1 deletion internal/http/webdriver.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ type sessionStartResponse struct {
func NewWebdriver(url string, creds iam.Credentials, timeout time.Duration) Webdriver {
return Webdriver{
HTTPClient: &http.Client{
Timeout: timeout,
Timeout: timeout,
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// Sauce can queue up Job start requests for up to 10 minutes and sends redirects in the meantime to
// keep the connection alive. A redirect is sent every 45 seconds.
Expand Down