]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package request |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "strings" | |
6 | ) | |
7 | ||
8 | // A Handlers provides a collection of request handlers for various | |
9 | // stages of handling requests. | |
10 | type Handlers struct { | |
11 | Validate HandlerList | |
12 | Build HandlerList | |
13 | Sign HandlerList | |
14 | Send HandlerList | |
15 | ValidateResponse HandlerList | |
16 | Unmarshal HandlerList | |
15c0b25d | 17 | UnmarshalStream HandlerList |
bae9f6d2 JC |
18 | UnmarshalMeta HandlerList |
19 | UnmarshalError HandlerList | |
20 | Retry HandlerList | |
21 | AfterRetry HandlerList | |
107c1cdb | 22 | CompleteAttempt HandlerList |
bae9f6d2 JC |
23 | Complete HandlerList |
24 | } | |
25 | ||
26 | // Copy returns of this handler's lists. | |
27 | func (h *Handlers) Copy() Handlers { | |
28 | return Handlers{ | |
29 | Validate: h.Validate.copy(), | |
30 | Build: h.Build.copy(), | |
31 | Sign: h.Sign.copy(), | |
32 | Send: h.Send.copy(), | |
33 | ValidateResponse: h.ValidateResponse.copy(), | |
34 | Unmarshal: h.Unmarshal.copy(), | |
15c0b25d | 35 | UnmarshalStream: h.UnmarshalStream.copy(), |
bae9f6d2 JC |
36 | UnmarshalError: h.UnmarshalError.copy(), |
37 | UnmarshalMeta: h.UnmarshalMeta.copy(), | |
38 | Retry: h.Retry.copy(), | |
39 | AfterRetry: h.AfterRetry.copy(), | |
107c1cdb | 40 | CompleteAttempt: h.CompleteAttempt.copy(), |
bae9f6d2 JC |
41 | Complete: h.Complete.copy(), |
42 | } | |
43 | } | |
44 | ||
45 | // Clear removes callback functions for all handlers | |
46 | func (h *Handlers) Clear() { | |
47 | h.Validate.Clear() | |
48 | h.Build.Clear() | |
49 | h.Send.Clear() | |
50 | h.Sign.Clear() | |
51 | h.Unmarshal.Clear() | |
15c0b25d | 52 | h.UnmarshalStream.Clear() |
bae9f6d2 JC |
53 | h.UnmarshalMeta.Clear() |
54 | h.UnmarshalError.Clear() | |
55 | h.ValidateResponse.Clear() | |
56 | h.Retry.Clear() | |
57 | h.AfterRetry.Clear() | |
107c1cdb | 58 | h.CompleteAttempt.Clear() |
bae9f6d2 JC |
59 | h.Complete.Clear() |
60 | } | |
61 | ||
863486a6 AG |
62 | // IsEmpty returns if there are no handlers in any of the handlerlists. |
63 | func (h *Handlers) IsEmpty() bool { | |
64 | if h.Validate.Len() != 0 { | |
65 | return false | |
66 | } | |
67 | if h.Build.Len() != 0 { | |
68 | return false | |
69 | } | |
70 | if h.Send.Len() != 0 { | |
71 | return false | |
72 | } | |
73 | if h.Sign.Len() != 0 { | |
74 | return false | |
75 | } | |
76 | if h.Unmarshal.Len() != 0 { | |
77 | return false | |
78 | } | |
79 | if h.UnmarshalStream.Len() != 0 { | |
80 | return false | |
81 | } | |
82 | if h.UnmarshalMeta.Len() != 0 { | |
83 | return false | |
84 | } | |
85 | if h.UnmarshalError.Len() != 0 { | |
86 | return false | |
87 | } | |
88 | if h.ValidateResponse.Len() != 0 { | |
89 | return false | |
90 | } | |
91 | if h.Retry.Len() != 0 { | |
92 | return false | |
93 | } | |
94 | if h.AfterRetry.Len() != 0 { | |
95 | return false | |
96 | } | |
97 | if h.CompleteAttempt.Len() != 0 { | |
98 | return false | |
99 | } | |
100 | if h.Complete.Len() != 0 { | |
101 | return false | |
102 | } | |
103 | ||
104 | return true | |
105 | } | |
106 | ||
bae9f6d2 JC |
107 | // A HandlerListRunItem represents an entry in the HandlerList which |
108 | // is being run. | |
109 | type HandlerListRunItem struct { | |
110 | Index int | |
111 | Handler NamedHandler | |
112 | Request *Request | |
113 | } | |
114 | ||
115 | // A HandlerList manages zero or more handlers in a list. | |
116 | type HandlerList struct { | |
117 | list []NamedHandler | |
118 | ||
119 | // Called after each request handler in the list is called. If set | |
120 | // and the func returns true the HandlerList will continue to iterate | |
121 | // over the request handlers. If false is returned the HandlerList | |
122 | // will stop iterating. | |
123 | // | |
124 | // Should be used if extra logic to be performed between each handler | |
125 | // in the list. This can be used to terminate a list's iteration | |
126 | // based on a condition such as error like, HandlerListStopOnError. | |
127 | // Or for logging like HandlerListLogItem. | |
128 | AfterEachFn func(item HandlerListRunItem) bool | |
129 | } | |
130 | ||
131 | // A NamedHandler is a struct that contains a name and function callback. | |
132 | type NamedHandler struct { | |
133 | Name string | |
134 | Fn func(*Request) | |
135 | } | |
136 | ||
137 | // copy creates a copy of the handler list. | |
138 | func (l *HandlerList) copy() HandlerList { | |
139 | n := HandlerList{ | |
140 | AfterEachFn: l.AfterEachFn, | |
141 | } | |
142 | if len(l.list) == 0 { | |
143 | return n | |
144 | } | |
145 | ||
146 | n.list = append(make([]NamedHandler, 0, len(l.list)), l.list...) | |
147 | return n | |
148 | } | |
149 | ||
150 | // Clear clears the handler list. | |
151 | func (l *HandlerList) Clear() { | |
152 | l.list = l.list[0:0] | |
153 | } | |
154 | ||
155 | // Len returns the number of handlers in the list. | |
156 | func (l *HandlerList) Len() int { | |
157 | return len(l.list) | |
158 | } | |
159 | ||
160 | // PushBack pushes handler f to the back of the handler list. | |
161 | func (l *HandlerList) PushBack(f func(*Request)) { | |
162 | l.PushBackNamed(NamedHandler{"__anonymous", f}) | |
163 | } | |
164 | ||
165 | // PushBackNamed pushes named handler f to the back of the handler list. | |
166 | func (l *HandlerList) PushBackNamed(n NamedHandler) { | |
167 | if cap(l.list) == 0 { | |
168 | l.list = make([]NamedHandler, 0, 5) | |
169 | } | |
170 | l.list = append(l.list, n) | |
171 | } | |
172 | ||
173 | // PushFront pushes handler f to the front of the handler list. | |
174 | func (l *HandlerList) PushFront(f func(*Request)) { | |
175 | l.PushFrontNamed(NamedHandler{"__anonymous", f}) | |
176 | } | |
177 | ||
178 | // PushFrontNamed pushes named handler f to the front of the handler list. | |
179 | func (l *HandlerList) PushFrontNamed(n NamedHandler) { | |
180 | if cap(l.list) == len(l.list) { | |
181 | // Allocating new list required | |
182 | l.list = append([]NamedHandler{n}, l.list...) | |
183 | } else { | |
184 | // Enough room to prepend into list. | |
185 | l.list = append(l.list, NamedHandler{}) | |
186 | copy(l.list[1:], l.list) | |
187 | l.list[0] = n | |
188 | } | |
189 | } | |
190 | ||
191 | // Remove removes a NamedHandler n | |
192 | func (l *HandlerList) Remove(n NamedHandler) { | |
193 | l.RemoveByName(n.Name) | |
194 | } | |
195 | ||
196 | // RemoveByName removes a NamedHandler by name. | |
197 | func (l *HandlerList) RemoveByName(name string) { | |
198 | for i := 0; i < len(l.list); i++ { | |
199 | m := l.list[i] | |
200 | if m.Name == name { | |
201 | // Shift array preventing creating new arrays | |
202 | copy(l.list[i:], l.list[i+1:]) | |
203 | l.list[len(l.list)-1] = NamedHandler{} | |
204 | l.list = l.list[:len(l.list)-1] | |
205 | ||
206 | // decrement list so next check to length is correct | |
207 | i-- | |
208 | } | |
209 | } | |
210 | } | |
211 | ||
9b12e4fe JC |
212 | // SwapNamed will swap out any existing handlers with the same name as the |
213 | // passed in NamedHandler returning true if handlers were swapped. False is | |
214 | // returned otherwise. | |
215 | func (l *HandlerList) SwapNamed(n NamedHandler) (swapped bool) { | |
216 | for i := 0; i < len(l.list); i++ { | |
217 | if l.list[i].Name == n.Name { | |
218 | l.list[i].Fn = n.Fn | |
219 | swapped = true | |
220 | } | |
221 | } | |
222 | ||
223 | return swapped | |
224 | } | |
225 | ||
15c0b25d AP |
226 | // Swap will swap out all handlers matching the name passed in. The matched |
227 | // handlers will be swapped in. True is returned if the handlers were swapped. | |
228 | func (l *HandlerList) Swap(name string, replace NamedHandler) bool { | |
229 | var swapped bool | |
230 | ||
231 | for i := 0; i < len(l.list); i++ { | |
232 | if l.list[i].Name == name { | |
233 | l.list[i] = replace | |
234 | swapped = true | |
235 | } | |
236 | } | |
237 | ||
238 | return swapped | |
239 | } | |
240 | ||
9b12e4fe JC |
241 | // SetBackNamed will replace the named handler if it exists in the handler list. |
242 | // If the handler does not exist the handler will be added to the end of the list. | |
243 | func (l *HandlerList) SetBackNamed(n NamedHandler) { | |
244 | if !l.SwapNamed(n) { | |
245 | l.PushBackNamed(n) | |
246 | } | |
247 | } | |
248 | ||
249 | // SetFrontNamed will replace the named handler if it exists in the handler list. | |
250 | // If the handler does not exist the handler will be added to the beginning of | |
251 | // the list. | |
252 | func (l *HandlerList) SetFrontNamed(n NamedHandler) { | |
253 | if !l.SwapNamed(n) { | |
254 | l.PushFrontNamed(n) | |
255 | } | |
256 | } | |
257 | ||
bae9f6d2 JC |
258 | // Run executes all handlers in the list with a given request object. |
259 | func (l *HandlerList) Run(r *Request) { | |
260 | for i, h := range l.list { | |
261 | h.Fn(r) | |
262 | item := HandlerListRunItem{ | |
263 | Index: i, Handler: h, Request: r, | |
264 | } | |
265 | if l.AfterEachFn != nil && !l.AfterEachFn(item) { | |
266 | return | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
271 | // HandlerListLogItem logs the request handler and the state of the | |
272 | // request's Error value. Always returns true to continue iterating | |
273 | // request handlers in a HandlerList. | |
274 | func HandlerListLogItem(item HandlerListRunItem) bool { | |
275 | if item.Request.Config.Logger == nil { | |
276 | return true | |
277 | } | |
278 | item.Request.Config.Logger.Log("DEBUG: RequestHandler", | |
279 | item.Index, item.Handler.Name, item.Request.Error) | |
280 | ||
281 | return true | |
282 | } | |
283 | ||
284 | // HandlerListStopOnError returns false to stop the HandlerList iterating | |
285 | // over request handlers if Request.Error is not nil. True otherwise | |
286 | // to continue iterating. | |
287 | func HandlerListStopOnError(item HandlerListRunItem) bool { | |
288 | return item.Request.Error == nil | |
289 | } | |
290 | ||
291 | // WithAppendUserAgent will add a string to the user agent prefixed with a | |
292 | // single white space. | |
293 | func WithAppendUserAgent(s string) Option { | |
294 | return func(r *Request) { | |
295 | r.Handlers.Build.PushBack(func(r2 *Request) { | |
296 | AddToUserAgent(r, s) | |
297 | }) | |
298 | } | |
299 | } | |
300 | ||
301 | // MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request | |
302 | // header. If the extra parameters are provided they will be added as metadata to the | |
303 | // name/version pair resulting in the following format. | |
304 | // "name/version (extra0; extra1; ...)" | |
305 | // The user agent part will be concatenated with this current request's user agent string. | |
306 | func MakeAddToUserAgentHandler(name, version string, extra ...string) func(*Request) { | |
307 | ua := fmt.Sprintf("%s/%s", name, version) | |
308 | if len(extra) > 0 { | |
309 | ua += fmt.Sprintf(" (%s)", strings.Join(extra, "; ")) | |
310 | } | |
311 | return func(r *Request) { | |
312 | AddToUserAgent(r, ua) | |
313 | } | |
314 | } | |
315 | ||
316 | // MakeAddToUserAgentFreeFormHandler adds the input to the User-Agent request header. | |
317 | // The input string will be concatenated with the current request's user agent string. | |
318 | func MakeAddToUserAgentFreeFormHandler(s string) func(*Request) { | |
319 | return func(r *Request) { | |
320 | AddToUserAgent(r, s) | |
321 | } | |
322 | } |