http.go raw

   1  package client
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/json"
   6  	"fmt"
   7  	"io"
   8  	"net/http"
   9  	"time"
  10  
  11  	"github.com/ultradns/ultradns-go-sdk/internal/version"
  12  	"github.com/ultradns/ultradns-go-sdk/pkg/errors"
  13  )
  14  
  15  const contentType = "application/json"
  16  const throttleSleep = 1 * time.Second
  17  const maxThrottleRetry = 3
  18  
  19  var (
  20  	defaultUserAgent = version.GetSDKVersion()
  21  )
  22  
  23  func (c *Client) Do(method, path string, payload interface{}, target *Response) (*http.Response, error) {
  24  	url := fmt.Sprintf("%s/%s", c.baseURL, path)
  25  	body := new(bytes.Buffer)
  26  
  27  	if payload != nil {
  28  		err := json.NewEncoder(body).Encode(payload)
  29  
  30  		if err != nil {
  31  			return nil, err
  32  		}
  33  	}
  34  
  35  	req, err := http.NewRequest(method, url, body)
  36  
  37  	if err != nil {
  38  		return nil, err
  39  	}
  40  
  41  	userAgent := defaultUserAgent + ";" + c.userAgent
  42  
  43  	req.Header.Set("Content-Type", contentType)
  44  	req.Header.Add("Accept", contentType)
  45  	req.Header.Add("User-Agent", userAgent)
  46  
  47  	c.logHttpRequest(req)
  48  	res, err := c.httpClient.Do(req)
  49  	c.logHttpResponse(res)
  50  
  51  	resp := &http.Response{}
  52  
  53  	if res != nil {
  54  		resp.Status = res.Status
  55  		resp.StatusCode = res.StatusCode
  56  		resp.Header = res.Header
  57  	}
  58  
  59  	if target == nil {
  60  		return resp, errors.ResponseTargetError("<nil>")
  61  	}
  62  
  63  	if resp.StatusCode == http.StatusTooManyRequests && target.retry < maxThrottleRetry {
  64  		c.Warn("Throttling the request: '%s %s': attempt=%v ", method, url, (target.retry + 1))
  65  		target.retry += 1
  66  		time.Sleep(throttleSleep)
  67  		return c.Do(method, path, payload, target)
  68  	}
  69  
  70  	if err != nil {
  71  		return resp, err
  72  	}
  73  
  74  	defer res.Body.Close()
  75  
  76  	er := c.validateResponse(res, target)
  77  
  78  	if er != nil {
  79  		return resp, er
  80  	}
  81  
  82  	return resp, nil
  83  }
  84  
  85  func (c *Client) validateResponse(res *http.Response, target *Response) error {
  86  
  87  	if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusMultipleChoices {
  88  		if res.StatusCode == http.StatusNoContent {
  89  			return nil
  90  		}
  91  
  92  		err := json.NewDecoder(res.Body).Decode(&target.Data)
  93  
  94  		if err != nil {
  95  			return err
  96  		}
  97  	} else {
  98  		bodyBytes, err := io.ReadAll(res.Body)
  99  
 100  		if err != nil {
 101  			return err
 102  		}
 103  
 104  		err = json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&target.ErrorList)
 105  
 106  		if err == nil {
 107  			return errors.APIResponseError(target.ErrorList[0].String())
 108  		}
 109  
 110  		c.Warn("Unable to parse API error message: %s", err.Error())
 111  
 112  		err = json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&target.Error)
 113  
 114  		if err == nil {
 115  			return errors.APIResponseError(target.Error.String())
 116  		}
 117  
 118  		return err
 119  	}
 120  
 121  	return nil
 122  }
 123