github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+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 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 }