github.com/ungtb10d/git-lfs@v2.5.2+incompatible/lfsapi/proxy.go (about) 1 package lfsapi 2 3 import ( 4 "net/http" 5 "net/url" 6 "strings" 7 8 "github.com/git-lfs/git-lfs/config" 9 10 "fmt" 11 ) 12 13 // Logic is copied, with small changes, from "net/http".ProxyFromEnvironment in the go std lib. 14 func proxyFromClient(c *Client) func(req *http.Request) (*url.URL, error) { 15 return func(req *http.Request) (*url.URL, error) { 16 httpsProxy, httpProxy, noProxy := getProxyServers(req.URL, c.uc, c.osEnv) 17 18 var proxy string 19 if req.URL.Scheme == "https" { 20 proxy = httpsProxy 21 } 22 23 if len(proxy) == 0 { 24 proxy = httpProxy 25 } 26 27 if len(proxy) == 0 { 28 return nil, nil 29 } 30 31 if !useProxy(noProxy, canonicalAddr(req.URL)) { 32 return nil, nil 33 } 34 35 proxyURL, err := url.Parse(proxy) 36 if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") { 37 // proxy was bogus. Try prepending "http://" to it and 38 // see if that parses correctly. If not, we fall 39 // through and complain about the original one. 40 if httpProxyURL, httpErr := url.Parse("http://" + proxy); httpErr == nil { 41 return httpProxyURL, nil 42 } 43 } 44 if err != nil { 45 return nil, fmt.Errorf("invalid proxy address: %q: %v", proxy, err) 46 } 47 return proxyURL, nil 48 } 49 } 50 51 func getProxyServers(u *url.URL, urlCfg *config.URLConfig, osEnv config.Environment) (httpsProxy string, httpProxy string, noProxy string) { 52 if osEnv == nil { 53 return 54 } 55 56 if len(httpsProxy) == 0 { 57 httpsProxy, _ = osEnv.Get("HTTPS_PROXY") 58 } 59 60 if len(httpsProxy) == 0 { 61 httpsProxy, _ = osEnv.Get("https_proxy") 62 } 63 64 if len(httpProxy) == 0 { 65 httpProxy, _ = osEnv.Get("HTTP_PROXY") 66 } 67 68 if len(httpProxy) == 0 { 69 httpProxy, _ = osEnv.Get("http_proxy") 70 } 71 72 if urlCfg != nil { 73 gitProxy, ok := urlCfg.Get("http", u.String(), "proxy") 74 if len(gitProxy) > 0 && ok { 75 if strings.HasPrefix(gitProxy, "https://") { 76 httpsProxy = gitProxy 77 } 78 httpProxy = gitProxy 79 } 80 } 81 82 noProxy, _ = osEnv.Get("NO_PROXY") 83 if len(noProxy) == 0 { 84 noProxy, _ = osEnv.Get("no_proxy") 85 } 86 87 return 88 } 89 90 // canonicalAddr returns url.Host but always with a ":port" suffix 91 // Copied from "net/http".ProxyFromEnvironment in the go std lib. 92 func canonicalAddr(url *url.URL) string { 93 addr := url.Host 94 if !hasPort(addr) { 95 return addr + ":" + portMap[url.Scheme] 96 } 97 return addr 98 } 99 100 // useProxy reports whether requests to addr should use a proxy, 101 // according to the noProxy or noProxy environment variable. 102 // addr is always a canonicalAddr with a host and port. 103 // Copied from "net/http".ProxyFromEnvironment in the go std lib 104 // and adapted to allow proxy usage even for localhost. 105 func useProxy(noProxy, addr string) bool { 106 if len(addr) == 0 { 107 return true 108 } 109 110 if noProxy == "*" { 111 return false 112 } 113 114 addr = strings.ToLower(strings.TrimSpace(addr)) 115 if hasPort(addr) { 116 addr = addr[:strings.LastIndex(addr, ":")] 117 } 118 119 for _, p := range strings.Split(noProxy, ",") { 120 p = strings.ToLower(strings.TrimSpace(p)) 121 if len(p) == 0 { 122 continue 123 } 124 if hasPort(p) { 125 p = p[:strings.LastIndex(p, ":")] 126 } 127 if addr == p { 128 return false 129 } 130 if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) { 131 // noProxy ".foo.com" matches "bar.foo.com" or "foo.com" 132 return false 133 } 134 if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' { 135 // noProxy "foo.com" matches "bar.foo.com" 136 return false 137 } 138 } 139 return true 140 } 141 142 // Given a string of the form "host", "host:port", or "[ipv6::address]:port", 143 // return true if the string includes a port. 144 // Copied from "net/http".ProxyFromEnvironment in the go std lib. 145 func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } 146 147 var ( 148 portMap = map[string]string{ 149 "http": "80", 150 "https": "443", 151 } 152 )