6dfe9766e0
Currently, we prepend "http://" to the proxy URL if it's not an HTTP or HTTPS URL. Unfortunately, that breaks support for SOCKS 5 proxies, which use URLs that start with "socks5://". Fix this by allowing URL schemes starting with "socks" in addition to those starting with "http". Note that this does not introduce any support for socks5h proxies, since Go does not support them, but we will support them automatically once Go does.
152 lines
3.6 KiB
Go
152 lines
3.6 KiB
Go
package lfshttp
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/git-lfs/git-lfs/config"
|
|
)
|
|
|
|
// Logic is copied, with small changes, from "net/http".ProxyFromEnvironment in the go std lib.
|
|
func proxyFromClient(c *Client) func(req *http.Request) (*url.URL, error) {
|
|
return func(req *http.Request) (*url.URL, error) {
|
|
httpsProxy, httpProxy, noProxy := getProxyServers(req.URL, c.uc, c.osEnv)
|
|
|
|
var proxy string
|
|
if req.URL.Scheme == "https" {
|
|
proxy = httpsProxy
|
|
}
|
|
|
|
if len(proxy) == 0 {
|
|
proxy = httpProxy
|
|
}
|
|
|
|
if len(proxy) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
if !useProxy(noProxy, canonicalAddr(req.URL)) {
|
|
return nil, nil
|
|
}
|
|
|
|
proxyURL, err := url.Parse(proxy)
|
|
if err != nil || !(strings.HasPrefix(proxyURL.Scheme, "http") || strings.HasPrefix(proxyURL.Scheme, "socks")) {
|
|
// proxy was bogus. Try prepending "http://" to it and
|
|
// see if that parses correctly. If not, we fall
|
|
// through and complain about the original one.
|
|
if httpProxyURL, httpErr := url.Parse("http://" + proxy); httpErr == nil {
|
|
return httpProxyURL, nil
|
|
}
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid proxy address: %q: %v", proxy, err)
|
|
}
|
|
return proxyURL, nil
|
|
}
|
|
}
|
|
|
|
func getProxyServers(u *url.URL, urlCfg *config.URLConfig, osEnv config.Environment) (httpsProxy string, httpProxy string, noProxy string) {
|
|
if osEnv == nil {
|
|
return
|
|
}
|
|
|
|
if len(httpsProxy) == 0 {
|
|
httpsProxy, _ = osEnv.Get("HTTPS_PROXY")
|
|
}
|
|
|
|
if len(httpsProxy) == 0 {
|
|
httpsProxy, _ = osEnv.Get("https_proxy")
|
|
}
|
|
|
|
if len(httpProxy) == 0 {
|
|
httpProxy, _ = osEnv.Get("HTTP_PROXY")
|
|
}
|
|
|
|
if len(httpProxy) == 0 {
|
|
httpProxy, _ = osEnv.Get("http_proxy")
|
|
}
|
|
|
|
if urlCfg != nil {
|
|
gitProxy, ok := urlCfg.Get("http", u.String(), "proxy")
|
|
if len(gitProxy) > 0 && ok {
|
|
if strings.HasPrefix(gitProxy, "https://") {
|
|
httpsProxy = gitProxy
|
|
}
|
|
httpProxy = gitProxy
|
|
}
|
|
}
|
|
|
|
noProxy, _ = osEnv.Get("NO_PROXY")
|
|
if len(noProxy) == 0 {
|
|
noProxy, _ = osEnv.Get("no_proxy")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// canonicalAddr returns url.Host but always with a ":port" suffix
|
|
// Copied from "net/http".ProxyFromEnvironment in the go std lib.
|
|
func canonicalAddr(url *url.URL) string {
|
|
addr := url.Host
|
|
if !hasPort(addr) {
|
|
return addr + ":" + portMap[url.Scheme]
|
|
}
|
|
return addr
|
|
}
|
|
|
|
// useProxy reports whether requests to addr should use a proxy,
|
|
// according to the noProxy or noProxy environment variable.
|
|
// addr is always a canonicalAddr with a host and port.
|
|
// Copied from "net/http".ProxyFromEnvironment in the go std lib
|
|
// and adapted to allow proxy usage even for localhost.
|
|
func useProxy(noProxy, addr string) bool {
|
|
if len(addr) == 0 {
|
|
return true
|
|
}
|
|
|
|
if noProxy == "*" {
|
|
return false
|
|
}
|
|
|
|
addr = strings.ToLower(strings.TrimSpace(addr))
|
|
if hasPort(addr) {
|
|
addr = addr[:strings.LastIndex(addr, ":")]
|
|
}
|
|
|
|
for _, p := range strings.Split(noProxy, ",") {
|
|
p = strings.ToLower(strings.TrimSpace(p))
|
|
if len(p) == 0 {
|
|
continue
|
|
}
|
|
if hasPort(p) {
|
|
p = p[:strings.LastIndex(p, ":")]
|
|
}
|
|
if addr == p {
|
|
return false
|
|
}
|
|
if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) {
|
|
// noProxy ".foo.com" matches "bar.foo.com" or "foo.com"
|
|
return false
|
|
}
|
|
if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' {
|
|
// noProxy "foo.com" matches "bar.foo.com"
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
|
|
// return true if the string includes a port.
|
|
// Copied from "net/http".ProxyFromEnvironment in the go std lib.
|
|
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
|
|
|
|
var (
|
|
portMap = map[string]string{
|
|
"http": "80",
|
|
"https": "443",
|
|
}
|
|
)
|