github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/api/plugin/plugin_connection.go (about)

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