140 lines
3.0 KiB
Go
140 lines
3.0 KiB
Go
|
package httputil
|
||
|
|
||
|
import (
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"strings"
|
||
|
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/github/git-lfs/config"
|
||
|
)
|
||
|
|
||
|
func ProxyFromGitConfigOrEnvironment(c *config.Configuration) func(req *http.Request) (*url.URL, error) {
|
||
|
https_proxy := c.Getenv("HTTPS_PROXY")
|
||
|
if len(https_proxy) == 0 {
|
||
|
https_proxy = c.Getenv("https_proxy")
|
||
|
}
|
||
|
|
||
|
http_proxy := c.Getenv("HTTP_PROXY")
|
||
|
if len(http_proxy) == 0 {
|
||
|
http_proxy = c.Getenv("http_proxy")
|
||
|
}
|
||
|
|
||
|
if len(http_proxy) == 0 {
|
||
|
http_proxy, _ = c.GitConfig("http.proxy")
|
||
|
}
|
||
|
|
||
|
no_proxy := c.Getenv("NO_PROXY")
|
||
|
if len(no_proxy) == 0 {
|
||
|
no_proxy = c.Getenv("no_proxy")
|
||
|
}
|
||
|
|
||
|
return func(req *http.Request) (*url.URL, error) {
|
||
|
var proxy string
|
||
|
if req.URL.Scheme == "https" {
|
||
|
proxy = https_proxy
|
||
|
}
|
||
|
|
||
|
if len(proxy) == 0 {
|
||
|
proxy = http_proxy
|
||
|
}
|
||
|
|
||
|
if len(proxy) == 0 {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
if !useProxy(no_proxy, canonicalAddr(req.URL)) {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
proxyURL, err := url.Parse(proxy)
|
||
|
if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") {
|
||
|
// 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 proxyURL, err := url.Parse("http://" + proxy); err == nil {
|
||
|
return proxyURL, nil
|
||
|
}
|
||
|
}
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("invalid proxy address: %q: %v", proxy, err)
|
||
|
}
|
||
|
return proxyURL, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// canonicalAddr returns url.Host but always with a ":port" suffix
|
||
|
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 NO_PROXY or no_proxy environment variable.
|
||
|
// addr is always a canonicalAddr with a host and port.
|
||
|
func useProxy(no_proxy, addr string) bool {
|
||
|
if len(addr) == 0 {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
if no_proxy == "*" {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
host, _, err := net.SplitHostPort(addr)
|
||
|
if err != nil {
|
||
|
return false
|
||
|
}
|
||
|
if host == "localhost" {
|
||
|
return false
|
||
|
}
|
||
|
if ip := net.ParseIP(host); ip != nil {
|
||
|
if ip.IsLoopback() {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
addr = strings.ToLower(strings.TrimSpace(addr))
|
||
|
if hasPort(addr) {
|
||
|
addr = addr[:strings.LastIndex(addr, ":")]
|
||
|
}
|
||
|
|
||
|
for _, p := range strings.Split(no_proxy, ",") {
|
||
|
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:]) {
|
||
|
// no_proxy ".foo.com" matches "bar.foo.com" or "foo.com"
|
||
|
return false
|
||
|
}
|
||
|
if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' {
|
||
|
// no_proxy "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.
|
||
|
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
|
||
|
|
||
|
var (
|
||
|
portMap = map[string]string{
|
||
|
"http": "80",
|
||
|
"https": "443",
|
||
|
}
|
||
|
)
|