github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/api/cloudcontroller/cloud_controller_connection.go (about) 1 package cloudcontroller 2 3 import ( 4 "crypto/x509" 5 "io/ioutil" 6 "net" 7 "net/http" 8 "net/url" 9 "strings" 10 "time" 11 12 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 13 "code.cloudfoundry.org/cli/util" 14 ) 15 16 // Config is for configuring a CloudControllerConnection. 17 type Config struct { 18 DialTimeout time.Duration 19 SkipSSLValidation bool 20 } 21 22 // CloudControllerConnection represents a connection to the Cloud Controller 23 // server. 24 type CloudControllerConnection struct { 25 HTTPClient *http.Client 26 UserAgent string 27 } 28 29 // NewConnection returns a new CloudControllerConnection with provided 30 // configuration. 31 func NewConnection(config Config) *CloudControllerConnection { 32 tr := &http.Transport{ 33 TLSClientConfig: util.NewTLSConfig(nil, config.SkipSSLValidation), 34 Proxy: http.ProxyFromEnvironment, 35 DialContext: (&net.Dialer{ 36 KeepAlive: 30 * time.Second, 37 Timeout: config.DialTimeout, 38 }).DialContext, 39 } 40 41 return &CloudControllerConnection{ 42 HTTPClient: &http.Client{Transport: tr}, 43 } 44 } 45 46 // Make performs the request and parses the response. 47 func (connection *CloudControllerConnection) Make(request *Request, passedResponse *Response) error { 48 // In case this function is called from a retry, passedResponse may already 49 // be populated with a previous response. We reset in case there's an HTTP 50 // error and we don't repopulate it in populateResponse. 51 passedResponse.reset() 52 53 response, err := connection.HTTPClient.Do(request.Request) 54 if err != nil { 55 return connection.processRequestErrors(request.Request, err) 56 } 57 58 return connection.populateResponse(response, passedResponse) 59 } 60 61 func (*CloudControllerConnection) handleStatusCodes(response *http.Response, passedResponse *Response) error { 62 if response.StatusCode == http.StatusNoContent { 63 passedResponse.RawResponse = []byte("{}") 64 } else { 65 rawBytes, err := ioutil.ReadAll(response.Body) 66 defer response.Body.Close() 67 if err != nil { 68 return err 69 } 70 71 passedResponse.RawResponse = rawBytes 72 } 73 74 if response.StatusCode >= 400 { 75 return ccerror.RawHTTPStatusError{ 76 StatusCode: response.StatusCode, 77 RawResponse: passedResponse.RawResponse, 78 RequestIDs: response.Header["X-Vcap-Request-Id"], 79 } 80 } 81 82 return nil 83 } 84 85 // handleWarnings looks for the "X-Cf-Warnings" header in the cloud controller 86 // response and URI decodes them. The value can contain multiple warnings that 87 // are comma separated. 88 func (*CloudControllerConnection) handleWarnings(response *http.Response) ([]string, error) { 89 rawWarnings := response.Header["X-Cf-Warnings"] 90 91 var warnings []string 92 for _, rawWarningsCommaSeparated := range rawWarnings { 93 for _, rawWarning := range strings.Split(rawWarningsCommaSeparated, ",") { 94 warning, err := url.QueryUnescape(rawWarning) 95 if err != nil { 96 return nil, err 97 } 98 warnings = append(warnings, strings.Trim(warning, " ")) 99 } 100 } 101 102 return warnings, nil 103 } 104 105 func (connection *CloudControllerConnection) populateResponse(response *http.Response, passedResponse *Response) error { 106 passedResponse.HTTPResponse = response 107 108 warnings, err := connection.handleWarnings(response) 109 if err != nil { 110 return err 111 } 112 passedResponse.Warnings = warnings 113 114 if resourceLocationURL := response.Header.Get("Location"); resourceLocationURL != "" { 115 passedResponse.ResourceLocationURL = resourceLocationURL 116 } 117 118 err = connection.handleStatusCodes(response, passedResponse) 119 if err != nil { 120 return err 121 } 122 123 if passedResponse.DecodeJSONResponseInto != nil { 124 err = DecodeJSON(passedResponse.RawResponse, passedResponse.DecodeJSONResponseInto) 125 if err != nil { 126 return err 127 } 128 } 129 130 return nil 131 } 132 133 func (*CloudControllerConnection) processRequestErrors(request *http.Request, err error) error { 134 switch e := err.(type) { 135 case *url.Error: 136 switch urlErr := e.Err.(type) { 137 case x509.UnknownAuthorityError: 138 return ccerror.UnverifiedServerError{ 139 URL: request.URL.String(), 140 } 141 case x509.HostnameError: 142 return ccerror.SSLValidationHostnameError{ 143 Message: urlErr.Error(), 144 } 145 default: 146 return ccerror.RequestError{Err: e} 147 } 148 default: 149 return err 150 } 151 }