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

     1  package router
     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  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    15  	"code.cloudfoundry.org/cli/api/router/routererror"
    16  )
    17  
    18  // ConnectionConfig is for configuring the RouterConnection
    19  type ConnectionConfig struct {
    20  	DialTimeout       time.Duration
    21  	SkipSSLValidation bool
    22  }
    23  
    24  // RouterConnection represents the connection to Router
    25  type RouterConnection struct {
    26  	HTTPClient *http.Client
    27  }
    28  
    29  // NewConnection returns a pointer to a new RouterConnection with the provided configuration
    30  func NewConnection(config ConnectionConfig) *RouterConnection {
    31  	tr := &http.Transport{
    32  		TLSClientConfig: &tls.Config{
    33  			InsecureSkipVerify: config.SkipSSLValidation,
    34  		},
    35  		Proxy: http.ProxyFromEnvironment,
    36  		DialContext: (&net.Dialer{
    37  			KeepAlive: 30 * time.Second,
    38  			Timeout:   config.DialTimeout,
    39  		}).DialContext,
    40  	}
    41  
    42  	return &RouterConnection{
    43  		HTTPClient: &http.Client{Transport: tr},
    44  	}
    45  }
    46  
    47  // Make performs the request and parses the response.
    48  func (connection *RouterConnection) Make(request *Request, responseToPopulate *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  	responseToPopulate.reset()
    53  
    54  	httpResponse, err := connection.HTTPClient.Do(request.Request)
    55  	if err != nil {
    56  		// request could not be made, e.g., ssl handshake or tcp dial timeout
    57  		return connection.processRequestErrors(request.Request, err)
    58  	}
    59  
    60  	return connection.populateResponse(httpResponse, responseToPopulate)
    61  }
    62  
    63  func (*RouterConnection) handleStatusCodes(httpResponse *http.Response, responseToPopulate *Response) error {
    64  	if httpResponse.StatusCode >= 400 {
    65  		return routererror.RawHTTPStatusError{
    66  			StatusCode:  httpResponse.StatusCode,
    67  			RawResponse: responseToPopulate.RawResponse,
    68  		}
    69  	}
    70  	return nil
    71  }
    72  
    73  func (connection *RouterConnection) populateResponse(httpResponse *http.Response, responseToPopulate *Response) error {
    74  	responseToPopulate.HTTPResponse = httpResponse
    75  
    76  	rawBytes, err := ioutil.ReadAll(httpResponse.Body)
    77  	defer httpResponse.Body.Close()
    78  	if err != nil {
    79  		return err
    80  	}
    81  	responseToPopulate.RawResponse = rawBytes
    82  
    83  	err = connection.handleStatusCodes(httpResponse, responseToPopulate)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	if responseToPopulate.Result != nil {
    89  		decoder := json.NewDecoder(bytes.NewBuffer(responseToPopulate.RawResponse))
    90  		decoder.UseNumber()
    91  		err = decoder.Decode(responseToPopulate.Result)
    92  		if err != nil {
    93  			return err
    94  		}
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  func (*RouterConnection) processRequestErrors(request *http.Request, err error) error {
   101  	switch e := err.(type) {
   102  	case *url.Error:
   103  		switch urlErr := e.Err.(type) {
   104  		case x509.UnknownAuthorityError:
   105  			return ccerror.UnverifiedServerError{
   106  				URL: request.URL.String(),
   107  			}
   108  		case x509.HostnameError:
   109  			return ccerror.SSLValidationHostnameError{
   110  				Message: urlErr.Error(),
   111  			}
   112  		default:
   113  			return ccerror.RequestError{Err: e}
   114  		}
   115  	default:
   116  		return err
   117  	}
   118  }