github.com/CyCoreSystems/ari@v4.8.4+incompatible/client/native/request.go (about)

     1  package native
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"net/http"
     8  	"time"
     9  
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  // MaxIdleConnections is the maximum number of idle web client
    14  // connections to maintain.
    15  var MaxIdleConnections = 20
    16  
    17  // RequestTimeout describes the maximum amount of time to wait
    18  // for a response to any request.
    19  var RequestTimeout = 2 * time.Second
    20  
    21  // RequestError describes an error with an error Code.
    22  type RequestError interface {
    23  	error
    24  	Code() int
    25  }
    26  
    27  type requestError struct {
    28  	statusCode int
    29  	text       string
    30  }
    31  
    32  // Error returns the request error as a string.
    33  func (e *requestError) Error() string {
    34  	return e.text
    35  }
    36  
    37  // Code returns the status code from the request.
    38  func (e *requestError) Code() int {
    39  	return e.statusCode
    40  }
    41  
    42  // CodeFromError extracts and returns the code from an error, or
    43  // 0 if not found.
    44  func CodeFromError(err error) int {
    45  	if reqerr, ok := err.(RequestError); ok {
    46  		return reqerr.Code()
    47  	}
    48  	return 0
    49  }
    50  
    51  func maybeRequestError(resp *http.Response) RequestError {
    52  	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
    53  		// 2xx response: All good.
    54  		return nil
    55  	}
    56  	return &requestError{
    57  		text:       "Non-2XX response: " + resp.Status,
    58  		statusCode: resp.StatusCode,
    59  	}
    60  }
    61  
    62  // MissingParams is an error message response emitted when a request
    63  // does not contain required parameters
    64  type MissingParams struct {
    65  	//Message
    66  	Type   string   `json:"type"`
    67  	Params []string `json:"params"` // List of missing parameters which are required
    68  }
    69  
    70  // get calls the ARI server with a GET request
    71  func (c *Client) get(url string, resp interface{}) error {
    72  
    73  	url = c.Options.URL + url
    74  
    75  	return c.makeRequest("GET", url, resp, nil)
    76  }
    77  
    78  // post calls the ARI server with a POST request.
    79  func (c *Client) post(requestURL string, resp interface{}, req interface{}) error {
    80  	url := c.Options.URL + requestURL
    81  	return c.makeRequest("POST", url, resp, req)
    82  }
    83  
    84  // put calls the ARI server with a PUT request.
    85  func (c *Client) put(url string, resp interface{}, req interface{}) error {
    86  
    87  	url = c.Options.URL + url
    88  
    89  	return c.makeRequest("PUT", url, resp, req)
    90  }
    91  
    92  // del calls the ARI server with a DELETE request
    93  func (c *Client) del(url string, resp interface{}, req string) error {
    94  
    95  	url = c.Options.URL + url
    96  	if req != "" {
    97  		url = url + "?" + req
    98  	}
    99  
   100  	return c.makeRequest("DELETE", url, resp, nil)
   101  }
   102  
   103  func (c *Client) makeRequest(method, url string, resp interface{}, req interface{}) (err error) {
   104  	var reqBody io.Reader
   105  	if req != nil {
   106  		reqBody, err = structToRequestBody(req)
   107  		if err != nil {
   108  			return errors.Wrap(err, "failed to marshal request")
   109  		}
   110  	}
   111  
   112  	var r *http.Request
   113  	r, err = http.NewRequest(method, url, reqBody)
   114  	if err != nil {
   115  		return errors.Wrap(err, "failed to create request")
   116  	}
   117  	r.Header.Set("Content-Type", "application/json")
   118  
   119  	if c.Options.Username != "" {
   120  		r.SetBasicAuth(c.Options.Username, c.Options.Password)
   121  	}
   122  
   123  	ret, err := c.httpClient.Do(r)
   124  	if err != nil {
   125  		return errors.Wrap(err, "failed to make request")
   126  	}
   127  	defer ret.Body.Close() //nolint:errcheck
   128  
   129  	if resp != nil {
   130  		err = json.NewDecoder(ret.Body).Decode(resp)
   131  		if err != nil {
   132  			return errors.Wrap(err, "failed to decode response")
   133  		}
   134  	}
   135  
   136  	return maybeRequestError(ret)
   137  }
   138  
   139  func structToRequestBody(req interface{}) (io.Reader, error) {
   140  	buf := new(bytes.Buffer)
   141  	if req != nil {
   142  		if err := json.NewEncoder(buf).Encode(req); err != nil {
   143  			return nil, err
   144  		}
   145  	}
   146  
   147  	return buf, nil
   148  }