-
Notifications
You must be signed in to change notification settings - Fork 0
/
request.go
174 lines (151 loc) · 4.28 KB
/
request.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
package tinyclient
import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"net/url"
"reflect"
"strings"
"time"
)
// Method is a type
type Method string
// Supported HTTP methods, for preventing typos
const (
Get Method = "GET"
Post Method = "POST"
Put Method = "PUT"
Patch Method = "PATCH"
Delete Method = "DELETE"
)
type Request struct {
client *Client
//HttpRequest is exposed because of missing cases of this client wrapper and so a professional user can handle for this edge
HttpRequest *http.Request
URL string
Method Method
Body interface{}
//bodyBytes is not exposed, it is for holding Body interface internally as bytes and reading the body without spoiling HttpRequest.GetBody
bodyBytes []byte
Cookies []*http.Cookie
Headers map[string]string
QueryParams map[string]string
FormData url.Values
SentAt time.Time
useSSL bool
}
func (request *Request) SetBody(body interface{}) *Request {
request.Body = body
return request
}
func (request *Request) SetMethod(method Method) *Request {
request.Method = method
return request
}
func (request *Request) SetURL(url string) *Request {
request.URL = url
return request
}
func (request *Request) AddHeader(header, value string) *Request {
request.Headers[header] = value
return request
}
// AddHeaders sets request headers
func (request *Request) AddHeaders(headers map[string]string) *Request {
for k, v := range headers {
request.Headers[k] = v
}
return request
}
// SetContentType sets content type of request
func (request *Request) SetContentType(contentType string) *Request {
request.Headers[ContentType] = contentType
return request
}
// ReadBody reads already set bodyBytes field from Request which is wrapper of *http.Request
func (request *Request) ReadBody() ([]byte, error) {
if len(request.bodyBytes) != 0 {
return request.bodyBytes, nil
}
err := errors.New("bodyBytes is empty")
request.client.ErrorLogger.Println(err)
return nil, err
}
func (request *Request) AddQueryParam(param, value string) *Request {
request.QueryParams[param] = value
return request
}
func (request *Request) AddQueryParams(params map[string]string) *Request {
for k, v := range params {
request.AddQueryParam(k, v)
}
return request
}
//parseRequestBody logics can't be in Request because of checking contentType
func (request *Request) parseRequestBody() (err error) {
contentType := request.Headers[ContentType]
if request.Body == nil {
return
}
kind := reflect.TypeOf(request.Body).Kind()
//http.Request.Body is io.ReadCloser and implements io.Reader too
//a server can put http.Request.Body into Request.Body
//it can be any other stream too
if reader, ok := request.Body.(io.Reader); ok {
request.bodyBytes, err = ioutil.ReadAll(reader)
if err != nil {
request.client.ErrorLogger.Println(err)
return err
}
} else if b, ok := request.Body.([]byte); ok {
request.bodyBytes = b
} else if s, ok := request.Body.(string); ok {
request.bodyBytes = []byte(s)
} else if IsJSONType(contentType) {
if kind == reflect.Struct || kind == reflect.Map || kind == reflect.Slice {
b, err := json.Marshal(request.Body)
if err != nil {
request.client.ErrorLogger.Println(err)
return err
}
request.bodyBytes = b
} else if kind == reflect.Ptr {
err := errors.New("Request body is pointer which we don't support for now, please use the value at the pointer as body")
request.client.ErrorLogger.Println(err)
return err
}
}
return
}
func (request *Request) generateURL() (*url.URL, error) {
v := strings.ToLower(request.URL)
if strings.HasPrefix(v, "http://") {
request.URL = strings.TrimPrefix(request.URL, "http://")
} else if strings.HasPrefix(v, "https://") {
request.URL = strings.TrimPrefix(request.URL, "https://")
}
// Generate prefix
prefix := "http://"
if request.useSSL {
prefix = "https://"
}
if len(request.QueryParams) != 0 {
//Create new URL with query params
params := url.Values{}
for key, value := range request.QueryParams {
params.Add(key, value)
}
request.URL = request.URL + "?" + params.Encode()
}
// Merge prefix and URL
URL := prefix + request.URL
// Parse URL
parsedURL, err := url.Parse(URL)
if err != nil {
request.client.ErrorLogger.Println(err)
return nil, err
}
return parsedURL, nil
}