github.com/arunkumar7540/cli@v6.45.0+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 func (*PluginConnection) handleStatusCodes(response *http.Response, passedResponse *Response) error { 65 if response.StatusCode >= 400 { 66 return pluginerror.RawHTTPStatusError{ 67 Status: response.Status, 68 RawResponse: passedResponse.RawResponse, 69 } 70 } 71 72 return nil 73 } 74 75 func (connection *PluginConnection) populateResponse(response *http.Response, passedResponse *Response, body io.ReadCloser) error { 76 passedResponse.HTTPResponse = response 77 78 rawBytes, err := ioutil.ReadAll(body) 79 defer body.Close() 80 if err != nil { 81 return err 82 } 83 passedResponse.RawResponse = rawBytes 84 85 err = connection.handleStatusCodes(response, passedResponse) 86 if err != nil { 87 return err 88 } 89 90 if passedResponse.Result != nil { 91 decoder := json.NewDecoder(bytes.NewBuffer(passedResponse.RawResponse)) 92 decoder.UseNumber() 93 err = decoder.Decode(passedResponse.Result) 94 if err != nil { 95 return err 96 } 97 } 98 99 return nil 100 } 101 102 // processRequestError handles errors that occur while making the request. 103 func (connection *PluginConnection) processRequestErrors(request *http.Request, err error) error { 104 switch e := err.(type) { 105 case *url.Error: 106 switch urlErr := e.Err.(type) { 107 case x509.UnknownAuthorityError: 108 return pluginerror.UnverifiedServerError{ 109 URL: request.URL.String(), 110 } 111 case x509.HostnameError: 112 return pluginerror.SSLValidationHostnameError{ 113 Message: urlErr.Error(), 114 } 115 default: 116 return pluginerror.RequestError{Err: e} 117 } 118 default: 119 return err 120 } 121 }