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 }