github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/api/uaa/uaa_connection.go (about)

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