-
Notifications
You must be signed in to change notification settings - Fork 7
/
request.go
179 lines (152 loc) · 4 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
175
176
177
178
179
package sawyer
import (
"github.com/lostisland/go-sawyer/mediatype"
"io/ioutil"
"net/http"
"net/url"
)
// Request is a wrapped net/http Request with a pointer to the net/http Client,
// MediaType, parsed URI query, and the configured Cacher. Requests are capable
// of returning a sawyer Response with Do() or the HTTP verb helpers (Get(),
// Head(), Post(), etc).
type Request struct {
Client *http.Client
MediaType *mediatype.MediaType
Query url.Values
Cacher Cacher
*http.Request
}
// NewRequest creates a new sawyer.Request for the given relative url path, with
// any default headers or query parameters specified on Client. The Request URL
// is resolved to an absolute URL.
func (c *Client) NewRequest(rawurl string) (*Request, error) {
httpreq, err := buildRequest(c, rawurl)
if httpreq == nil {
return nil, err
}
return &Request{c.HttpClient, nil, httpreq.URL.Query(), c.Cacher, httpreq}, err
}
// Do completes the HTTP request, returning a response. The Request's Cacher is
// used to return a cached response if available. Otherwise, the request goes
// through and fills the cache for future requests.
func (r *Request) Do(method string) *Response {
r.URL.RawQuery = r.Query.Encode()
r.Method = method
cacher := r.Cacher
cacheBehavior := r.cacherBehavior()
if cacheBehavior != useCache {
cacher = noOpCacher
}
cached, cachedErr := cacher.Get(r.Request)
if cachedErr == nil {
if cached.IsFresh() {
return cached.Decode(r)
} else {
cached.SetupRequest(r.Request)
}
}
httpres, err := r.Client.Do(r.Request)
if err != nil {
return ResponseError(err)
}
if cachedErr == nil && cacheBehavior == useCache && httpres.StatusCode == 304 {
cacher.UpdateCache(r.Request, httpres)
return cached.Decode(r)
}
mtype, err := mediaType(httpres)
if err != nil {
httpres.Body.Close()
return ResponseError(err)
}
res := &Response{
MediaType: mtype,
BodyClosed: false,
Response: httpres,
Cacher: cacher,
isApiError: UseApiError(httpres.StatusCode),
}
if !res.AnyError() {
if cacheBehavior == resetCache {
r.Cacher.Reset(r.Request)
} else if cacheBehavior == clearCache {
r.Cacher.Clear(r.Request)
} else {
cacher.Set(r.Request, res)
}
}
return res
}
// Head is a helper method for Do().
func (r *Request) Head() *Response {
return r.Do(HeadMethod)
}
// Get is a helper method for Do().
func (r *Request) Get() *Response {
return r.Do(GetMethod)
}
// Post is a helper method for Do().
func (r *Request) Post() *Response {
return r.Do(PostMethod)
}
// Put is a helper method for Do().
func (r *Request) Put() *Response {
return r.Do(PutMethod)
}
// Patch is a helper method for Do().
func (r *Request) Patch() *Response {
return r.Do(PatchMethod)
}
// Delete is a helper method for Do().
func (r *Request) Delete() *Response {
return r.Do(DeleteMethod)
}
// Options is a helper method for Do().
func (r *Request) Options() *Response {
return r.Do(OptionsMethod)
}
// SetBody encodes and sets the proper headers for the request body from the
// given resource. The resource is encoded in-memory, so be careful about
// passing a massive object. You can set the ContentLength and Body properties
// manually.
func (r *Request) SetBody(mtype *mediatype.MediaType, resource interface{}) error {
r.MediaType = mtype
r.Header.Set(ctypeHeader, mtype.String())
if resource == nil {
return nil
}
buf, err := mtype.Encode(resource)
if err != nil {
return err
}
r.ContentLength = int64(buf.Len())
r.Body = ioutil.NopCloser(buf)
return nil
}
func (r *Request) cacherBehavior() int {
switch r.Method {
case GetMethod:
return useCache
case HeadMethod, OptionsMethod:
return noCache
case DeleteMethod:
return clearCache
default:
return resetCache
}
}
const (
noCache = iota
useCache = iota
resetCache = iota
clearCache = iota
)
const (
ctypeHeader = "Content-Type"
HeadMethod = "HEAD"
GetMethod = "GET"
PostMethod = "POST"
PutMethod = "PUT"
PatchMethod = "PATCH"
DeleteMethod = "DELETE"
OptionsMethod = "OPTIONS"
)