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

     1  package plugin
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/x509"
     6  	"encoding/json"
     7  	"io"
     8  	"io/ioutil"
     9  	"net"
    10  	"net/http"
    11  	"net/url"
    12  	"time"
    13  
    14  	"code.cloudfoundry.org/cli/api/plugin/pluginerror"
    15  	"code.cloudfoundry.org/cli/util"
    16  )
    17  
    18  // PluginConnection represents a connection to a plugin repo.
    19  type PluginConnection struct {
    20  	HTTPClient  *http.Client
    21  	proxyReader ProxyReader // nolint
    22  }
    23  
    24  // NewConnection returns a new PluginConnection
    25  func NewConnection(skipSSLValidation bool, dialTimeout time.Duration) *PluginConnection {
    26  	tr := &http.Transport{
    27  		TLSClientConfig: util.NewTLSConfig(nil, skipSSLValidation),
    28  		Proxy:           http.ProxyFromEnvironment,
    29  		DialContext: (&net.Dialer{
    30  			KeepAlive: 30 * time.Second,
    31  			Timeout:   dialTimeout,
    32  		}).DialContext,
    33  	}
    34  
    35  	return &PluginConnection{
    36  		HTTPClient: &http.Client{Transport: tr},
    37  	}
    38  }
    39  
    40  // Make performs the request and parses the response.
    41  func (connection *PluginConnection) Make(request *http.Request, passedResponse *Response, proxyReader ProxyReader) error {
    42  	// In case this function is called from a retry, passedResponse may already
    43  	// be populated with a previous response. We reset in case there's an HTTP
    44  	// error and we don't repopulate it in populateResponse.
    45  	passedResponse.reset()
    46  
    47  	response, err := connection.HTTPClient.Do(request)
    48  	if err != nil {
    49  		return connection.processRequestErrors(request, err)
    50  	}
    51  
    52  	body := response.Body
    53  	if proxyReader != nil {
    54  		proxyReader.Start(response.ContentLength)
    55  		defer proxyReader.Finish()
    56  		body = proxyReader.Wrap(response.Body)
    57  	}
    58  
    59  	return connection.populateResponse(response, passedResponse, body)
    60  }
    61  
    62  func (*PluginConnection) handleStatusCodes(response *http.Response, passedResponse *Response) error {
    63  	if response.StatusCode >= 400 {
    64  		return pluginerror.RawHTTPStatusError{
    65  			Status:      response.Status,
    66  			RawResponse: passedResponse.RawResponse,
    67  		}
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  func (connection *PluginConnection) populateResponse(response *http.Response, passedResponse *Response, body io.ReadCloser) error {
    74  	passedResponse.HTTPResponse = response
    75  
    76  	rawBytes, err := ioutil.ReadAll(body)
    77  	defer 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  // processRequestError handles errors that occur while making the request.
   101  func (connection *PluginConnection) processRequestErrors(request *http.Request, err error) error {
   102  	switch e := err.(type) {
   103  	case *url.Error:
   104  		switch urlErr := e.Err.(type) {
   105  		case x509.UnknownAuthorityError:
   106  			return pluginerror.UnverifiedServerError{
   107  				URL: request.URL.String(),
   108  			}
   109  		case x509.HostnameError:
   110  			return pluginerror.SSLValidationHostnameError{
   111  				Message: urlErr.Error(),
   112  			}
   113  		default:
   114  			return pluginerror.RequestError{Err: e}
   115  		}
   116  	default:
   117  		return err
   118  	}
   119  }