-
-
Notifications
You must be signed in to change notification settings - Fork 183
Add Windows proxy autodetection #222
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
Open
durck
wants to merge
1
commit into
NHAS:main
Choose a base branch
from
durck:fix/wininet-auto-proxy-fallback
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| package client | ||
|
|
||
| import ( | ||
| "strings" | ||
| "unicode" | ||
| ) | ||
|
|
||
| type proxyCandidate struct { | ||
| forScheme string | ||
| proxyURL string | ||
| } | ||
|
|
||
| type systemProxyConfig struct { | ||
| enabled bool | ||
| candidates []proxyCandidate | ||
| } | ||
|
|
||
| type autoProxySelection struct { | ||
| proxyAddr string | ||
| fallbackProxies []string | ||
| orderedProxies []string | ||
| } | ||
|
|
||
| // parseWinINETProxyString parses the value of the WinINET ProxyServer | ||
| // registry value. Common formats: | ||
| // | ||
| // "host:port" — single proxy for all schemes | ||
| // "http=h:p;https=h:p;ftp=h:p;socks=h:p" — per-scheme list | ||
| // "http=http://h:p https=socks5://s:p" — explicit proxy schemes | ||
| // | ||
| // Entries can be separated by semicolons or whitespace. ftp= entries are | ||
| // ignored because the client never makes FTP requests. If a per-scheme entry | ||
| // omits an explicit proxy URL scheme, Windows' HTTP/Secure fields are treated | ||
| // as HTTP proxy endpoints, while the SOCKS field is treated as SOCKS5. | ||
| func parseWinINETProxyString(raw string) []proxyCandidate { | ||
| raw = strings.TrimSpace(raw) | ||
| if raw == "" { | ||
| return nil | ||
| } | ||
|
|
||
| var candidates []proxyCandidate | ||
| for _, part := range splitWinINETProxyList(raw) { | ||
| forScheme, proxySpec := splitWinINETProxyEntry(part) | ||
| proxyURL := normalizeWinINETProxySpec(forScheme, proxySpec) | ||
| if proxyURL == "" { | ||
| continue | ||
| } | ||
|
|
||
| if forScheme == "ftp" { | ||
| continue | ||
| } | ||
|
|
||
| candidates = append(candidates, proxyCandidate{ | ||
| forScheme: forScheme, | ||
| proxyURL: proxyURL, | ||
| }) | ||
| } | ||
|
|
||
| return candidates | ||
| } | ||
|
|
||
| func splitWinINETProxyList(raw string) []string { | ||
| return strings.FieldsFunc(raw, func(r rune) bool { | ||
| return r == ';' || unicode.IsSpace(r) | ||
| }) | ||
| } | ||
|
|
||
| func splitWinINETProxyEntry(entry string) (forScheme, proxySpec string) { | ||
| kv := strings.SplitN(strings.TrimSpace(entry), "=", 2) | ||
| if len(kv) != 2 { | ||
| return "", strings.TrimSpace(entry) | ||
| } | ||
|
|
||
| forScheme = strings.ToLower(strings.TrimSpace(kv[0])) | ||
| if forScheme == "secure" { | ||
| forScheme = "https" | ||
| } | ||
|
|
||
| return forScheme, strings.TrimSpace(kv[1]) | ||
| } | ||
|
|
||
| func normalizeWinINETProxySpec(forScheme, proxySpec string) string { | ||
| proxySpec = strings.TrimSpace(proxySpec) | ||
| if proxySpec == "" { | ||
| return "" | ||
| } | ||
|
|
||
| lowerSpec := strings.ToLower(proxySpec) | ||
| if lowerSpec == "direct" || lowerSpec == "direct://" { | ||
| return "" | ||
| } | ||
|
|
||
| if scheme, ok := explicitProxyScheme(proxySpec); ok { | ||
| switch scheme { | ||
| case "http", "https", "socks", "socks5": | ||
| return proxySpec | ||
| default: | ||
| return "" | ||
| } | ||
| } | ||
|
|
||
| if forScheme == "socks" { | ||
| return "socks5://" + proxySpec | ||
| } | ||
|
|
||
| return "http://" + proxySpec | ||
| } | ||
|
|
||
| func explicitProxyScheme(proxySpec string) (string, bool) { | ||
| index := strings.Index(proxySpec, "://") | ||
| if index <= 0 { | ||
| return "", false | ||
| } | ||
|
|
||
| return strings.ToLower(proxySpec[:index]), true | ||
| } | ||
|
|
||
| func orderProxyCandidatesForTransport(candidates []proxyCandidate, transport string) []string { | ||
| var preference []string | ||
|
|
||
| switch transport { | ||
| case "http", "ws": | ||
| preference = []string{"http", "", "https", "socks"} | ||
| case "https", "tls", "wss": | ||
| preference = []string{"https", "", "http", "socks"} | ||
| default: | ||
| preference = []string{"", "https", "http", "socks"} | ||
| } | ||
|
|
||
| var ordered []string | ||
| seen := map[string]bool{} | ||
|
|
||
| for _, scheme := range preference { | ||
| for _, candidate := range candidates { | ||
| if candidate.forScheme != scheme || seen[candidate.proxyURL] { | ||
| continue | ||
| } | ||
|
|
||
| ordered = append(ordered, candidate.proxyURL) | ||
| seen[candidate.proxyURL] = true | ||
| } | ||
| } | ||
|
|
||
| return ordered | ||
| } | ||
|
|
||
| func selectAutoDetectedProxies(config systemProxyConfig, transport, configuredProxy string) autoProxySelection { | ||
| ordered := orderProxyCandidatesForTransport(config.candidates, transport) | ||
| selection := autoProxySelection{ | ||
| proxyAddr: configuredProxy, | ||
| orderedProxies: ordered, | ||
| } | ||
|
|
||
| if config.enabled && configuredProxy == "" && len(ordered) > 0 { | ||
| selection.proxyAddr = ordered[0] | ||
| selection.fallbackProxies = ordered[1:] | ||
| return selection | ||
| } | ||
|
|
||
| selection.fallbackProxies = ordered | ||
| return selection | ||
| } | ||
|
|
||
| func dedupeProxyFallbacks(primary string, fallbacks []string) []string { | ||
| seen := map[string]bool{} | ||
| if key := proxyDedupeKey(primary); key != "" { | ||
| seen[key] = true | ||
| } | ||
|
|
||
| var deduped []string | ||
| for _, fallback := range fallbacks { | ||
| key := proxyDedupeKey(fallback) | ||
| if key == "" || seen[key] { | ||
| continue | ||
| } | ||
|
|
||
| deduped = append(deduped, fallback) | ||
| seen[key] = true | ||
| } | ||
|
|
||
| return deduped | ||
| } | ||
|
|
||
| func proxyDedupeKey(proxy string) string { | ||
| normalized, err := GetProxyDetails(proxy) | ||
| if err == nil { | ||
| return normalized | ||
| } | ||
|
|
||
| return strings.TrimSpace(proxy) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| //go:build !windows | ||
|
|
||
| package client | ||
|
|
||
| // DetectSystemProxy is a no-op on non-Windows platforms. WinINET-style proxy | ||
| // auto-detection is not available; users on other platforms should rely on | ||
| // the standard *_PROXY environment variables, which Run() already consults. | ||
| func DetectSystemProxy() (string, error) { | ||
| return "", nil | ||
| } | ||
|
|
||
| func detectSystemProxyConfig() (systemProxyConfig, error) { | ||
| return systemProxyConfig{}, nil | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This is very windows focused, remember that linux exists too and has its own set of http_proxy variables.
Its also a little bit cluttered here