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 }