forked from LoginRadius/go-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
httprutils.go
186 lines (157 loc) · 4.78 KB
/
httprutils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// The httputils package holds functions and structs for making RESTful API calls
package httprutils
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/url"
"time"
"github.com/LoginRadius/go-sdk/lrerror"
)
type Method string
const (
Get Method = "GET"
Post Method = "POST"
Put Method = "PUT"
Delete Method = "DELETE"
)
// Request holds the request to an API Call.
type Request struct {
Method Method
URL string
Headers map[string]string
QueryParams map[string]string
Body *bytes.Buffer
}
// DefaultClient is used if no custom HTTP client is defined
// http.Client configures a timeout that short-circuits long-running connections.
// The default for this value is 0, which is interpreted as “no timeout”, meaning if no custom client
// with a TimeOut value specified is defined, in case of API outage the GO program will continue to hang
// A custom TimeoutClient is defined below.
var DefaultClient = &Client{HTTPClient: http.DefaultClient}
// Client allows modification of client headers, redirect policy
// and other settings
// See https://golang.org/pkg/net/http
type Client struct {
HTTPClient *http.Client
}
// Response holds the response from an API call.
type Response struct {
StatusCode int
Body string
Headers map[string][]string
OrigBody []byte
}
// BuildRequestObject creates the HTTP request object.
func BuildRequestObject(request Request) (*http.Request, error) {
// Add any query parameters to the URL.
if len(request.QueryParams) != 0 {
request.URL = AddQueryParams(request.URL, request.QueryParams)
}
if request.Body == nil {
encodedBody, _ := EncodeBody("")
request.Body = encodedBody
}
req, err := http.NewRequest(string(request.Method), request.URL, request.Body)
if err != nil {
err = lrerror.New("EncodingError", "Error constructing http request", err)
return req, err
}
for key, value := range request.Headers {
req.Header.Set(key, value)
}
return req, nil
}
// MakeRequest makes the API call.
func MakeRequest(req *http.Request) (*http.Response, error) {
return DefaultClient.HTTPClient.Do(req)
}
// BuildResponse builds the response struct.
func BuildResponse(res *http.Response) (*Response, error) {
response := Response{}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
err := lrerror.New("EncodingError", "Error reading the response body", err)
return nil, err
}
if res.StatusCode < 200 || res.StatusCode > 299 {
err = lrerror.New("LoginradiusRespondedWithError", "Received error response from Loginradius", errors.New(string(body)))
} else {
response = Response{
StatusCode: res.StatusCode,
Body: string(body),
Headers: res.Header,
OrigBody: body,
}
}
res.Body.Close()
return &response, err
}
func Send(request Request) (*Response, error) {
return DefaultClient.Send(request)
}
// The following functions enable the ability to define a
// custom HTTP Client
// MakeRequest makes the API call.
func (c *Client) MakeRequest(req *http.Request) (*http.Response, error) {
return c.HTTPClient.Do(req)
}
// Send will build your request, make the request, and build your response.
func (c *Client) Send(request Request) (*Response, error) {
// Build the HTTP request object.
req, err := BuildRequestObject(request)
if err != nil {
return nil, err
}
// Build the HTTP client and make the request.
res, err := c.MakeRequest(req)
if err != nil {
err := lrerror.New("MakeRequestError", "Error making the request", err)
return nil, err
}
// Build Response object.
return BuildResponse(res)
}
var tr = &http.Transport{
MaxIdleConns: 100,
MaxConnsPerHost: 100,
MaxIdleConnsPerHost: 100,
}
// Custom client with time out after 8 seconds
var NetClient = &http.Client{
Timeout: time.Second * 8,
Transport: tr,
}
var TimeoutClient = &Client{HTTPClient: NetClient}
// EncodeBody takes an interface and returns a *bytes.Buffer suitable for making RESTful requests with
func EncodeBody(body interface{}) (*bytes.Buffer, error) {
buffer := new(bytes.Buffer)
asserted, ok := body.([]byte)
if ok {
var raw map[string]interface{}
json.Unmarshal(asserted, &raw)
encodeErr := json.NewEncoder(buffer).Encode(&raw)
if encodeErr != nil {
err := lrerror.New("EncodingError", "Error encoding the request body", encodeErr)
return nil, err
}
} else {
encodeErr := json.NewEncoder(buffer).Encode(body)
if encodeErr != nil {
err := lrerror.New("EncodingError", "Error encoding the request body", encodeErr)
return nil, err
}
}
return buffer, nil
}
// AddQueryParams takes a map of query params and appends each to the URL
func AddQueryParams(baseURL string, queryParams map[string]string) string {
baseURL += "?"
params := url.Values{}
for key, value := range queryParams {
params.Add(key, value)
}
return baseURL + params.Encode()
}