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  )