github.com/cloudfoundry/cli@v7.1.0+incompatible/api/uaa/uaa_connection.go (about)

     1  package uaa
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/x509"
     6  	"encoding/json"
     7  	"io/ioutil"
     8  	"net"
     9  	"net/http"
    10  	"net/url"
    11  	"time"
    12  
    13  	"code.cloudfoundry.org/cli/util"
    14  )
    15  
    16  // UAAConnection represents the connection to UAA
    17  type UAAConnection struct {
    18  	HTTPClient *http.Client
    19  }
    20  
    21  // NewConnection returns a pointer to a new UAA Connection
    22  func NewConnection(skipSSLValidation bool, disableKeepAlives bool, dialTimeout time.Duration) *UAAConnection {
    23  	tr := &http.Transport{
    24  		DialContext: (&net.Dialer{
    25  			KeepAlive: 30 * time.Second,
    26  			Timeout:   dialTimeout,
    27  		}).DialContext,
    28  		DisableKeepAlives: disableKeepAlives,
    29  		Proxy:             http.ProxyFromEnvironment,
    30  		TLSClientConfig:   util.NewTLSConfig(nil, skipSSLValidation),
    31  	}
    32  
    33  	return &UAAConnection{
    34  		HTTPClient: &http.Client{
    35  			Transport: tr,
    36  			CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
    37  				// This prevents redirects. When making a request to /oauth/authorize,
    38  				// the client should not follow redirects in order to obtain the ssh
    39  				// passcode.
    40  				return http.ErrUseLastResponse
    41  			},
    42  		},
    43  	}
    44  }
    45  
    46  // Make takes a passedRequest, converts it into an HTTP request and then
    47  // executes it. The response is then injected into passedResponse.
    48  func (connection *UAAConnection) Make(request *http.Request, passedResponse *Response) error {
    49  	// In case this function is called from a retry, passedResponse may already
    50  	// be populated with a previous response. We reset in case there's an HTTP
    51  	// error and we don't repopulate it in populateResponse.
    52  	passedResponse.reset()
    53  
    54  	response, err := connection.HTTPClient.Do(request)
    55  	if err != nil {
    56  		return connection.processRequestErrors(request, err)
    57  	}
    58  
    59  	return connection.populateResponse(response, passedResponse)
    60  }
    61  
    62  func (*UAAConnection) handleStatusCodes(response *http.Response, passedResponse *Response) error {
    63  	if response.StatusCode >= 400 {
    64  		return RawHTTPStatusError{
    65  			StatusCode:  response.StatusCode,
    66  			RawResponse: passedResponse.RawResponse,
    67  		}
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  func (connection *UAAConnection) populateResponse(response *http.Response, passedResponse *Response) error {
    74  	passedResponse.HTTPResponse = response
    75  
    76  	rawBytes, err := ioutil.ReadAll(response.Body)
    77  	defer response.Body.Close()
    78  	if err != nil {
    79  		return err
    80  	}
    81  	passedResponse.RawResponse = rawBytes
    82  
    83  	err = connection.handleStatusCodes(response, passedResponse)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	if passedResponse.Result != nil {
    89  		decoder := json.NewDecoder(bytes.NewBuffer(passedResponse.RawResponse))
    90  		decoder.UseNumber()
    91  		err = decoder.Decode(passedResponse.Result)
    92  		if err != nil {
    93  			return err
    94  		}
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  func (connection *UAAConnection) processRequestErrors(request *http.Request, err error) error {
   101  	switch e := err.(type) {
   102  	case *url.Error:
   103  		if _, ok := e.Err.(x509.UnknownAuthorityError); ok {
   104  			return UnverifiedServerError{
   105  				URL: request.URL.String(),
   106  			}
   107  		}
   108  		return RequestError{Err: e}
   109  	default:
   110  		return err
   111  	}
   112  }