github.com/lukasheimann/cloudfoundrycli@v7.1.0+incompatible/api/router/router_connection.go (about)

     1  package router
     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/api/cloudcontroller/ccerror"
    14  	"code.cloudfoundry.org/cli/api/router/routererror"
    15  	"code.cloudfoundry.org/cli/util"
    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: util.NewTLSConfig(nil, config.SkipSSLValidation),
    33  		Proxy:           http.ProxyFromEnvironment,
    34  		DialContext: (&net.Dialer{
    35  			KeepAlive: 30 * time.Second,
    36  			Timeout:   config.DialTimeout,
    37  		}).DialContext,
    38  	}
    39  
    40  	return &RouterConnection{
    41  		HTTPClient: &http.Client{Transport: tr},
    42  	}
    43  }
    44  
    45  // Make performs the request and parses the response.
    46  func (connection *RouterConnection) Make(request *Request, responseToPopulate *Response) error {
    47  	// In case this function is called from a retry, passedResponse may already
    48  	// be populated with a previous response. We reset in case there's an HTTP
    49  	// error and we don't repopulate it in populateResponse.
    50  	responseToPopulate.reset()
    51  
    52  	httpResponse, err := connection.HTTPClient.Do(request.Request)
    53  	if err != nil {
    54  		// request could not be made, e.g., ssl handshake or tcp dial timeout
    55  		return connection.processRequestErrors(request.Request, err)
    56  	}
    57  
    58  	return connection.populateResponse(httpResponse, responseToPopulate)
    59  }
    60  
    61  func (*RouterConnection) handleStatusCodes(httpResponse *http.Response, responseToPopulate *Response) error {
    62  	if httpResponse.StatusCode >= 400 {
    63  		return routererror.RawHTTPStatusError{
    64  			StatusCode:  httpResponse.StatusCode,
    65  			RawResponse: responseToPopulate.RawResponse,
    66  		}
    67  	}
    68  	return nil
    69  }
    70  
    71  func (connection *RouterConnection) populateResponse(httpResponse *http.Response, responseToPopulate *Response) error {
    72  	responseToPopulate.HTTPResponse = httpResponse
    73  
    74  	rawBytes, err := ioutil.ReadAll(httpResponse.Body)
    75  	defer httpResponse.Body.Close()
    76  	if err != nil {
    77  		return err
    78  	}
    79  	responseToPopulate.RawResponse = rawBytes
    80  
    81  	err = connection.handleStatusCodes(httpResponse, responseToPopulate)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	if responseToPopulate.Result != nil {
    87  		decoder := json.NewDecoder(bytes.NewBuffer(responseToPopulate.RawResponse))
    88  		decoder.UseNumber()
    89  		err = decoder.Decode(responseToPopulate.Result)
    90  		if err != nil {
    91  			return err
    92  		}
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  func (*RouterConnection) processRequestErrors(request *http.Request, err error) error {
    99  	switch e := err.(type) {
   100  	case *url.Error:
   101  		switch urlErr := e.Err.(type) {
   102  		case x509.UnknownAuthorityError:
   103  			return ccerror.UnverifiedServerError{
   104  				URL: request.URL.String(),
   105  			}
   106  		case x509.HostnameError:
   107  			return ccerror.SSLValidationHostnameError{
   108  				Message: urlErr.Error(),
   109  			}
   110  		default:
   111  			return ccerror.RequestError{Err: e}
   112  		}
   113  	default:
   114  		return err
   115  	}
   116  }