+ if req, err = http.NewRequest(method, u.String(), bytes.NewBufferString(vals.Encode())); err != nil {
+ return nil, err
+ }
+ req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+ }
+
+ // Add api token header
+ req.Header.Add("Authorization", fmt.Sprintf("OAuth2 %s", c.ApiToken))
+ if c.External {
+ req.Header.Add("X-External-Client", "1")
+ }
+
+ resp, err = c.Client.Do(req)
+ if err != nil {
+ if retryable {
+ time.Sleep(RetryDelay)
+ continue
+ } else {
+ return nil, err
+ }
+ }
+
+ if resp.StatusCode == http.StatusOK {
+ return resp.Body, nil
+ }
+
+ defer resp.Body.Close()
+
+ switch resp.StatusCode {
+ case 408, 409, 422, 423, 500, 502, 503, 504:
+ time.Sleep(RetryDelay)
+ continue
+ default:
+ return nil, newAPIServerError(c.ApiServer, resp)
+ }
+ }
+
+ if resp != nil {
+ return nil, newAPIServerError(c.ApiServer, resp)
+ }
+ return nil, err
+}
+
+func newAPIServerError(ServerAddress string, resp *http.Response) APIServerError {
+
+ ase := APIServerError{
+ ServerAddress: ServerAddress,
+ HttpStatusCode: resp.StatusCode,
+ HttpStatusMessage: resp.Status}
+
+ // If the response body has {"errors":["reason1","reason2"]}
+ // then return those reasons.
+ var errInfo = Dict{}
+ if err := json.NewDecoder(resp.Body).Decode(&errInfo); err == nil {
+ if errorList, ok := errInfo["errors"]; ok {
+ if errArray, ok := errorList.([]interface{}); ok {
+ for _, errItem := range errArray {
+ // We expect an array of strings here.
+ // Non-strings will be passed along
+ // JSON-encoded.
+ if s, ok := errItem.(string); ok {
+ ase.ErrorDetails = append(ase.ErrorDetails, s)
+ } else if j, err := json.Marshal(errItem); err == nil {
+ ase.ErrorDetails = append(ase.ErrorDetails, string(j))
+ }
+ }
+ }