github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+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 urlCfg != nil { 53 httpProxy, _ = urlCfg.Get("http", u.String(), "proxy") 54 if strings.HasPrefix(httpProxy, "https://") { 55 httpsProxy = httpProxy 56 } 57 } 58 59 if osEnv == nil { 60 return 61 } 62 63 if len(httpsProxy) == 0 { 64 httpsProxy, _ = osEnv.Get("HTTPS_PROXY") 65 } 66 67 if len(httpsProxy) == 0 { 68 httpsProxy, _ = osEnv.Get("https_proxy") 69 } 70 71 if len(httpProxy) == 0 { 72 httpProxy, _ = osEnv.Get("HTTP_PROXY") 73 } 74 75 if len(httpProxy) == 0 { 76 httpProxy, _ = osEnv.Get("http_proxy") 77 } 78 79 noProxy, _ = osEnv.Get("NO_PROXY") 80 if len(noProxy) == 0 { 81 noProxy, _ = osEnv.Get("no_proxy") 82 } 83 84 return 85 } 86 87 // canonicalAddr returns url.Host but always with a ":port" suffix 88 // Copied from "net/http".ProxyFromEnvironment in the go std lib. 89 func canonicalAddr(url *url.URL) string { 90 addr := url.Host 91 if !hasPort(addr) { 92 return addr + ":" + portMap[url.Scheme] 93 } 94 return addr 95 } 96 97 // useProxy reports whether requests to addr should use a proxy, 98 // according to the noProxy or noProxy environment variable. 99 // addr is always a canonicalAddr with a host and port. 100 // Copied from "net/http".ProxyFromEnvironment in the go std lib 101 // and adapted to allow proxy usage even for localhost. 102 func useProxy(noProxy, addr string) bool { 103 if len(addr) == 0 { 104 return true 105 } 106 107 if noProxy == "*" { 108 return false 109 } 110 111 addr = strings.ToLower(strings.TrimSpace(addr)) 112 if hasPort(addr) { 113 addr = addr[:strings.LastIndex(addr, ":")] 114 } 115 116 for _, p := range strings.Split(noProxy, ",") { 117 p = strings.ToLower(strings.TrimSpace(p)) 118 if len(p) == 0 { 119 continue 120 } 121 if hasPort(p) { 122 p = p[:strings.LastIndex(p, ":")] 123 } 124 if addr == p { 125 return false 126 } 127 if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) { 128 // noProxy ".foo.com" matches "bar.foo.com" or "foo.com" 129 return false 130 } 131 if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' { 132 // noProxy "foo.com" matches "bar.foo.com" 133 return false 134 } 135 } 136 return true 137 } 138 139 // Given a string of the form "host", "host:port", or "[ipv6::address]:port", 140 // return true if the string includes a port. 141 // Copied from "net/http".ProxyFromEnvironment in the go std lib. 142 func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } 143 144 var ( 145 portMap = map[string]string{ 146 "http": "80", 147 "https": "443", 148 } 149 )