github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/cf/net/http_client.go (about) 1 package net 2 3 import ( 4 _ "crypto/sha512" // #82254112: http://bridge.grumpy-troll.org/2014/05/golang-tls-comodo/ 5 "crypto/x509" 6 "fmt" 7 "net" 8 "net/http" 9 "net/url" 10 "strings" 11 asErrors "errors" 12 13 "code.cloudfoundry.org/cli/cf/errors" 14 . "code.cloudfoundry.org/cli/cf/i18n" 15 "golang.org/x/net/websocket" 16 ) 17 18 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . HTTPClientInterface 19 20 type HTTPClientInterface interface { 21 RequestDumperInterface 22 23 Do(*http.Request) (*http.Response, error) 24 ExecuteCheckRedirect(req *http.Request, via []*http.Request) error 25 } 26 27 type client struct { 28 *http.Client 29 dumper RequestDumper 30 } 31 32 var NewHTTPClient = func(tr *http.Transport, dumper RequestDumper) HTTPClientInterface { 33 c := client{ 34 &http.Client{ 35 Transport: tr, 36 }, 37 dumper, 38 } 39 c.CheckRedirect = c.checkRedirect 40 41 return &c 42 } 43 44 func (cl *client) ExecuteCheckRedirect(req *http.Request, via []*http.Request) error { 45 return cl.CheckRedirect(req, via) 46 } 47 48 func (cl *client) checkRedirect(req *http.Request, via []*http.Request) error { 49 if len(via) > 1 { 50 return errors.New(T("stopped after 1 redirect")) 51 } 52 53 prevReq := via[len(via)-1] 54 cl.copyHeaders(prevReq, req, getBaseDomain(req.URL.String()) == getBaseDomain(via[0].URL.String())) 55 cl.dumper.DumpRequest(req) 56 57 return nil 58 } 59 60 func (cl *client) copyHeaders(from *http.Request, to *http.Request, sameDomain bool) { 61 for key, values := range from.Header { 62 // do not copy POST-specific headers 63 if key != "Content-Type" && key != "Content-Length" && !(!sameDomain && key == "Authorization") { 64 to.Header.Set(key, strings.Join(values, ",")) 65 } 66 } 67 } 68 69 func (cl *client) DumpRequest(req *http.Request) { 70 cl.dumper.DumpRequest(req) 71 } 72 73 func (cl *client) DumpResponse(res *http.Response) { 74 cl.dumper.DumpResponse(res) 75 } 76 77 func WrapNetworkErrors(host string, err error) error { 78 var innerErr error 79 switch typedErr := err.(type) { 80 case *url.Error: 81 innerErr = typedErr.Err 82 case *websocket.DialError: 83 innerErr = typedErr.Err 84 } 85 86 if innerErr != nil { 87 if asErrors.As(innerErr, &x509.UnknownAuthorityError{}){ 88 return errors.NewInvalidSSLCert(host, T("unknown authority")) 89 } 90 if asErrors.As(innerErr, &x509.HostnameError{}){ 91 return errors.NewInvalidSSLCert(host, T("not valid for the requested host")) 92 } 93 if asErrors.As(innerErr, &x509.CertificateInvalidError{}){ 94 return errors.NewInvalidSSLCert(host, "") 95 } 96 typedInnerErr := new(net.OpError) 97 if asErrors.As(innerErr, &typedInnerErr) { 98 if typedInnerErr.Op == "dial" { 99 return fmt.Errorf("%s: %s\n%s", T("Error performing request"), err.Error(), T("TIP: If you are behind a firewall and require an HTTP proxy, verify the https_proxy environment variable is correctly set. Else, check your network connection.")) 100 } 101 } 102 } 103 104 return fmt.Errorf("%s: %s", T("Error performing request"), err.Error()) 105 } 106 107 func getBaseDomain(host string) string { 108 hostURL, _ := url.Parse(host) 109 hostStrs := strings.Split(hostURL.Host, ".") 110 return hostStrs[len(hostStrs)-2] + "." + hostStrs[len(hostStrs)-1] 111 }