code.cloudfoundry.org/cli@v7.1.0+incompatible/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  
    12  	"code.cloudfoundry.org/cli/cf/errors"
    13  	. "code.cloudfoundry.org/cli/cf/i18n"
    14  	"golang.org/x/net/websocket"
    15  )
    16  
    17  //go:generate counterfeiter . HTTPClientInterface
    18  
    19  type HTTPClientInterface interface {
    20  	RequestDumperInterface
    21  
    22  	Do(*http.Request) (*http.Response, error)
    23  	ExecuteCheckRedirect(req *http.Request, via []*http.Request) error
    24  }
    25  
    26  type client struct {
    27  	*http.Client
    28  	dumper RequestDumper
    29  }
    30  
    31  var NewHTTPClient = func(tr *http.Transport, dumper RequestDumper) HTTPClientInterface {
    32  	c := client{
    33  		&http.Client{
    34  			Transport: tr,
    35  		},
    36  		dumper,
    37  	}
    38  	c.CheckRedirect = c.checkRedirect
    39  
    40  	return &c
    41  }
    42  
    43  func (cl *client) ExecuteCheckRedirect(req *http.Request, via []*http.Request) error {
    44  	return cl.CheckRedirect(req, via)
    45  }
    46  
    47  func (cl *client) checkRedirect(req *http.Request, via []*http.Request) error {
    48  	if len(via) > 1 {
    49  		return errors.New(T("stopped after 1 redirect"))
    50  	}
    51  
    52  	prevReq := via[len(via)-1]
    53  	cl.copyHeaders(prevReq, req, getBaseDomain(req.URL.String()) == getBaseDomain(via[0].URL.String()))
    54  	cl.dumper.DumpRequest(req)
    55  
    56  	return nil
    57  }
    58  
    59  func (cl *client) copyHeaders(from *http.Request, to *http.Request, sameDomain bool) {
    60  	for key, values := range from.Header {
    61  		// do not copy POST-specific headers
    62  		if key != "Content-Type" && key != "Content-Length" && !(!sameDomain && key == "Authorization") {
    63  			to.Header.Set(key, strings.Join(values, ","))
    64  		}
    65  	}
    66  }
    67  
    68  func (cl *client) DumpRequest(req *http.Request) {
    69  	cl.dumper.DumpRequest(req)
    70  }
    71  
    72  func (cl *client) DumpResponse(res *http.Response) {
    73  	cl.dumper.DumpResponse(res)
    74  }
    75  
    76  func WrapNetworkErrors(host string, err error) error {
    77  	var innerErr error
    78  	switch typedErr := err.(type) {
    79  	case *url.Error:
    80  		innerErr = typedErr.Err
    81  	case *websocket.DialError:
    82  		innerErr = typedErr.Err
    83  	}
    84  
    85  	if innerErr != nil {
    86  		switch typedInnerErr := innerErr.(type) {
    87  		case x509.UnknownAuthorityError:
    88  			return errors.NewInvalidSSLCert(host, T("unknown authority"))
    89  		case x509.HostnameError:
    90  			return errors.NewInvalidSSLCert(host, T("not valid for the requested host"))
    91  		case x509.CertificateInvalidError:
    92  			return errors.NewInvalidSSLCert(host, "")
    93  		case *net.OpError:
    94  			if typedInnerErr.Op == "dial" {
    95  				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."))
    96  			}
    97  		}
    98  	}
    99  
   100  	return fmt.Errorf("%s: %s", T("Error performing request"), err.Error())
   101  }
   102  
   103  func getBaseDomain(host string) string {
   104  	hostURL, _ := url.Parse(host)
   105  	hostStrs := strings.Split(hostURL.Host, ".")
   106  	return hostStrs[len(hostStrs)-2] + "." + hostStrs[len(hostStrs)-1]
   107  }