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  )