diff --git a/cmd/claws/main.go b/cmd/claws/main.go index 4724a22c..4b1a468b 100644 --- a/cmd/claws/main.go +++ b/cmd/claws/main.go @@ -19,9 +19,10 @@ import ( var version = "dev" func main() { - // Parse command line flags opts := parseFlags() + propagateAllProxy() + // Apply CLI options to global config cfg := config.Global() @@ -102,30 +103,29 @@ func parseFlags() cliOptions { args := os.Args[1:] for i := 0; i < len(args); i++ { - arg := args[i] - switch { - case arg == "-p" || arg == "--profile": + switch args[i] { + case "-p", "--profile": if i+1 < len(args) { i++ opts.profile = args[i] } - case arg == "-r" || arg == "--region": + case "-r", "--region": if i+1 < len(args) { i++ opts.region = args[i] } - case arg == "-ro" || arg == "--read-only": + case "-ro", "--read-only": opts.readOnly = true - case arg == "-e" || arg == "--env": + case "-e", "--env": opts.envCreds = true - case arg == "-l" || arg == "--log-file": + case "-l", "--log-file": if i+1 < len(args) { i++ opts.logFile = args[i] } - case arg == "-h" || arg == "--help": + case "-h", "--help": showHelp = true - case arg == "-v" || arg == "--version": + case "-v", "--version": showVersion = true } } @@ -167,4 +167,36 @@ func printUsage() { fmt.Println() fmt.Println("Environment Variables:") fmt.Println(" CLAWS_READ_ONLY=1|true Enable read-only mode") + fmt.Println(" ALL_PROXY Propagated to HTTP_PROXY/HTTPS_PROXY if not set") +} + +// propagateAllProxy copies ALL_PROXY to HTTP_PROXY/HTTPS_PROXY if not set. +// Go's net/http ignores ALL_PROXY, so we propagate it to the standard vars. +func propagateAllProxy() { + allProxy := os.Getenv("ALL_PROXY") + if allProxy == "" { + return + } + + var propagated []string + + if os.Getenv("HTTPS_PROXY") == "" { + if err := os.Setenv("HTTPS_PROXY", allProxy); err != nil { + log.Warn("failed to set HTTPS_PROXY", "error", err) + } else { + propagated = append(propagated, "HTTPS_PROXY") + } + } + + if os.Getenv("HTTP_PROXY") == "" { + if err := os.Setenv("HTTP_PROXY", allProxy); err != nil { + log.Warn("failed to set HTTP_PROXY", "error", err) + } else { + propagated = append(propagated, "HTTP_PROXY") + } + } + + if len(propagated) > 0 { + log.Debug("propagated ALL_PROXY", "to", propagated) + } } diff --git a/cmd/claws/proxy_test.go b/cmd/claws/proxy_test.go new file mode 100644 index 00000000..06cfb8e4 --- /dev/null +++ b/cmd/claws/proxy_test.go @@ -0,0 +1,94 @@ +package main + +import ( + "os" + "testing" +) + +func TestPropagateAllProxy(t *testing.T) { + // Note: Only uppercase ALL_PROXY is supported. + // Lowercase all_proxy is intentionally not supported to match + // the AWS SDK behavior and keep the implementation simple. + // Go's net/http.ProxyFromEnvironment also only checks uppercase + // for HTTP_PROXY and HTTPS_PROXY on non-CGI environments. + + proxyVars := []string{ + "ALL_PROXY", + "HTTP_PROXY", + "HTTPS_PROXY", + } + + tests := []struct { + name string + envVars map[string]string + wantHTTPS string + wantHTTP string + }{ + { + name: "ALL_PROXY propagates to both HTTP and HTTPS", + envVars: map[string]string{"ALL_PROXY": "socks5h://proxy:1080"}, + wantHTTPS: "socks5h://proxy:1080", + wantHTTP: "socks5h://proxy:1080", + }, + { + name: "HTTPS_PROXY already set - only HTTP propagated", + envVars: map[string]string{"ALL_PROXY": "http://all", "HTTPS_PROXY": "http://existing"}, + wantHTTPS: "http://existing", + wantHTTP: "http://all", + }, + { + name: "HTTP_PROXY already set - only HTTPS propagated", + envVars: map[string]string{"ALL_PROXY": "http://all", "HTTP_PROXY": "http://existing"}, + wantHTTPS: "http://all", + wantHTTP: "http://existing", + }, + { + name: "both already set - no propagation", + envVars: map[string]string{"ALL_PROXY": "http://all", "HTTP_PROXY": "http://h", "HTTPS_PROXY": "http://s"}, + wantHTTPS: "http://s", + wantHTTP: "http://h", + }, + { + name: "no ALL_PROXY - no action", + envVars: map[string]string{}, + wantHTTPS: "", + wantHTTP: "", + }, + { + name: "lowercase all_proxy not supported", + envVars: map[string]string{"all_proxy": "http://proxy:8080"}, + wantHTTPS: "", + wantHTTP: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + clearEnvVars(t, proxyVars) + setEnvVars(t, tt.envVars) + + propagateAllProxy() + + if got := os.Getenv("HTTPS_PROXY"); got != tt.wantHTTPS { + t.Errorf("HTTPS_PROXY = %q, want %q", got, tt.wantHTTPS) + } + if got := os.Getenv("HTTP_PROXY"); got != tt.wantHTTP { + t.Errorf("HTTP_PROXY = %q, want %q", got, tt.wantHTTP) + } + }) + } +} + +func clearEnvVars(t *testing.T, keys []string) { + t.Helper() + for _, key := range keys { + os.Unsetenv(key) + } +} + +func setEnvVars(t *testing.T, vars map[string]string) { + t.Helper() + for key, value := range vars { + t.Setenv(key, value) + } +}