| 1 | // Package sysproxy resolves the OS-level proxy (Windows system/PAC settings) |
| 2 | // for a target URL. ForURL returns nil on platforms without system-proxy |
| 3 | // support or when no proxy applies, so callers fall back to direct/env. |
| 4 | package sysproxy |
| 5 | |
| 6 | import ( |
| 7 | "net/url" |
| 8 | "strings" |
| 9 | ) |
| 10 | |
| 11 | func splitList(s string) []string { |
| 12 | return strings.FieldsFunc(s, func(r rune) bool { |
| 13 | return r == ';' || r == ' ' || r == '\t' || r == '\n' || r == '\r' |
| 14 | }) |
| 15 | } |
| 16 | |
| 17 | // parseProxyList picks a proxy from a WinHTTP/IE proxy string for scheme. The |
| 18 | // string is either "host:port" (all protocols) or "http=h:p;https=h:p" form. |
| 19 | func parseProxyList(list, scheme string) *url.URL { |
| 20 | var fallback string |
| 21 | for _, f := range splitList(list) { |
| 22 | if i := strings.IndexByte(f, '='); i >= 0 { |
| 23 | if strings.EqualFold(f[:i], scheme) { |
| 24 | return hostProxyURL(f[i+1:]) |
| 25 | } |
| 26 | continue |
| 27 | } |
| 28 | if fallback == "" { |
| 29 | fallback = f |
| 30 | } |
| 31 | } |
| 32 | if fallback != "" { |
| 33 | return hostProxyURL(fallback) |
| 34 | } |
| 35 | return nil |
| 36 | } |
| 37 | |
| 38 | func hostProxyURL(hostport string) *url.URL { |
| 39 | hostport = strings.TrimSpace(hostport) |
| 40 | if i := strings.Index(hostport, "://"); i >= 0 { |
| 41 | hostport = hostport[i+3:] |
| 42 | } |
| 43 | if hostport == "" { |
| 44 | return nil |
| 45 | } |
| 46 | return &url.URL{Scheme: "http", Host: hostport} |
| 47 | } |
| 48 | |
| 49 | // bypassed reports whether host matches a WinINET proxy-bypass entry. "<local>" |
| 50 | // matches dotless (intranet) hosts; a leading "*" is a suffix wildcard. |
| 51 | func bypassed(host, bypass string) bool { |
| 52 | host = strings.ToLower(strings.TrimSpace(host)) |
| 53 | if host == "" { |
| 54 | return false |
| 55 | } |
| 56 | for _, e := range splitList(bypass) { |
| 57 | e = strings.ToLower(e) |
| 58 | switch { |
| 59 | case e == "<local>": |
| 60 | if !strings.Contains(host, ".") { |
| 61 | return true |
| 62 | } |
| 63 | case strings.HasPrefix(e, "*"): |
| 64 | if strings.HasSuffix(host, strings.TrimPrefix(e, "*")) { |
| 65 | return true |
| 66 | } |
| 67 | case host == e: |
| 68 | return true |
| 69 | } |
| 70 | } |
| 71 | return false |
| 72 | } |
| 73 |