]>
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 | ||
62 | // A HandlerListRunItem represents an entry in the HandlerList which | |
63 | // is being run. | |
64 | type HandlerListRunItem struct { | |
65 | Index int | |
66 | Handler NamedHandler | |
67 | Request *Request | |
68 | } | |
69 | ||
70 | // A HandlerList manages zero or more handlers in a list. | |
71 | type HandlerList struct { | |
72 | list []NamedHandler | |
73 | ||
74 | // Called after each request handler in the list is called. If set | |
75 | // and the func returns true the HandlerList will continue to iterate | |
76 | // over the request handlers. If false is returned the HandlerList | |
77 | // will stop iterating. | |
78 | // | |
79 | // Should be used if extra logic to be performed between each handler | |
80 | // in the list. This can be used to terminate a list's iteration | |
81 | // based on a condition such as error like, HandlerListStopOnError. | |
82 | // Or for logging like HandlerListLogItem. | |
83 | AfterEachFn func(item HandlerListRunItem) bool | |
84 | } | |
85 | ||
86 | // A NamedHandler is a struct that contains a name and function callback. | |
87 | type NamedHandler struct { | |
88 | Name string | |
89 | Fn func(*Request) | |
90 | } | |
91 | ||
92 | // copy creates a copy of the handler list. | |
93 | func (l *HandlerList) copy() HandlerList { | |
94 | n := HandlerList{ | |
95 | AfterEachFn: l.AfterEachFn, | |
96 | } | |
97 | if len(l.list) == 0 { | |
98 | return n | |
99 | } | |
100 | ||
101 | n.list = append(make([]NamedHandler, 0, len(l.list)), l.list...) | |
102 | return n | |
103 | } | |
104 | ||
105 | // Clear clears the handler list. | |
106 | func (l *HandlerList) Clear() { | |
107 | l.list = l.list[0:0] | |
108 | } | |
109 | ||
110 | // Len returns the number of handlers in the list. | |
111 | func (l *HandlerList) Len() int { | |
112 | return len(l.list) | |
113 | } | |
114 | ||
115 | // PushBack pushes handler f to the back of the handler list. | |
116 | func (l *HandlerList) PushBack(f func(*Request)) { | |
117 | l.PushBackNamed(NamedHandler{"__anonymous", f}) | |
118 | } | |
119 | ||
120 | // PushBackNamed pushes named handler f to the back of the handler list. | |
121 | func (l *HandlerList) PushBackNamed(n NamedHandler) { | |
122 | if cap(l.list) == 0 { | |
123 | l.list = make([]NamedHandler, 0, 5) | |
124 | } | |
125 | l.list = append(l.list, n) | |
126 | } | |
127 | ||
128 | // PushFront pushes handler f to the front of the handler list. | |
129 | func (l *HandlerList) PushFront(f func(*Request)) { | |
130 | l.PushFrontNamed(NamedHandler{"__anonymous", f}) | |
131 | } | |
132 | ||
133 | // PushFrontNamed pushes named handler f to the front of the handler list. | |
134 | func (l *HandlerList) PushFrontNamed(n NamedHandler) { | |
135 | if cap(l.list) == len(l.list) { | |
136 | // Allocating new list required | |
137 | l.list = append([]NamedHandler{n}, l.list...) | |
138 | } else { | |
139 | // Enough room to prepend into list. | |
140 | l.list = append(l.list, NamedHandler{}) | |
141 | copy(l.list[1:], l.list) | |
142 | l.list[0] = n | |
143 | } | |
144 | } | |
145 | ||
146 | // Remove removes a NamedHandler n | |
147 | func (l *HandlerList) Remove(n NamedHandler) { | |
148 | l.RemoveByName(n.Name) | |
149 | } | |
150 | ||
151 | // RemoveByName removes a NamedHandler by name. | |
152 | func (l *HandlerList) RemoveByName(name string) { | |
153 | for i := 0; i < len(l.list); i++ { | |
154 | m := l.list[i] | |
155 | if m.Name == name { | |
156 | // Shift array preventing creating new arrays | |
157 | copy(l.list[i:], l.list[i+1:]) | |
158 | l.list[len(l.list)-1] = NamedHandler{} | |
159 | l.list = l.list[:len(l.list)-1] | |
160 | ||
161 | // decrement list so next check to length is correct | |
162 | i-- | |
163 | } | |
164 | } | |
165 | } | |
166 | ||
9b12e4fe JC |
167 | // SwapNamed will swap out any existing handlers with the same name as the |
168 | // passed in NamedHandler returning true if handlers were swapped. False is | |
169 | // returned otherwise. | |
170 | func (l *HandlerList) SwapNamed(n NamedHandler) (swapped bool) { | |
171 | for i := 0; i < len(l.list); i++ { | |
172 | if l.list[i].Name == n.Name { | |
173 | l.list[i].Fn = n.Fn | |
174 | swapped = true | |
175 | } | |
176 | } | |
177 | ||
178 | return swapped | |
179 | } | |
180 | ||
15c0b25d AP |
181 | // Swap will swap out all handlers matching the name passed in. The matched |
182 | // handlers will be swapped in. True is returned if the handlers were swapped. | |
183 | func (l *HandlerList) Swap(name string, replace NamedHandler) bool { | |
184 | var swapped bool | |
185 | ||
186 | for i := 0; i < len(l.list); i++ { | |
187 | if l.list[i].Name == name { | |
188 | l.list[i] = replace | |
189 | swapped = true | |
190 | } | |
191 | } | |
192 | ||
193 | return swapped | |
194 | } | |
195 | ||
9b12e4fe JC |
196 | // SetBackNamed will replace the named handler if it exists in the handler list. |
197 | // If the handler does not exist the handler will be added to the end of the list. | |
198 | func (l *HandlerList) SetBackNamed(n NamedHandler) { | |
199 | if !l.SwapNamed(n) { | |
200 | l.PushBackNamed(n) | |
201 | } | |
202 | } | |
203 | ||
204 | // SetFrontNamed will replace the named handler if it exists in the handler list. | |
205 | // If the handler does not exist the handler will be added to the beginning of | |
206 | // the list. | |
207 | func (l *HandlerList) SetFrontNamed(n NamedHandler) { | |
208 | if !l.SwapNamed(n) { | |
209 | l.PushFrontNamed(n) | |
210 | } | |
211 | } | |
212 | ||
bae9f6d2 JC |
213 | // Run executes all handlers in the list with a given request object. |
214 | func (l *HandlerList) Run(r *Request) { | |
215 | for i, h := range l.list { | |
216 | h.Fn(r) | |
217 | item := HandlerListRunItem{ | |
218 | Index: i, Handler: h, Request: r, | |
219 | } | |
220 | if l.AfterEachFn != nil && !l.AfterEachFn(item) { | |
221 | return | |
222 | } | |
223 | } | |
224 | } | |
225 | ||
226 | // HandlerListLogItem logs the request handler and the state of the | |
227 | // request's Error value. Always returns true to continue iterating | |
228 | // request handlers in a HandlerList. | |
229 | func HandlerListLogItem(item HandlerListRunItem) bool { | |
230 | if item.Request.Config.Logger == nil { | |
231 | return true | |
232 | } | |
233 | item.Request.Config.Logger.Log("DEBUG: RequestHandler", | |
234 | item.Index, item.Handler.Name, item.Request.Error) | |
235 | ||
236 | return true | |
237 | } | |
238 | ||
239 | // HandlerListStopOnError returns false to stop the HandlerList iterating | |
240 | // over request handlers if Request.Error is not nil. True otherwise | |
241 | // to continue iterating. | |
242 | func HandlerListStopOnError(item HandlerListRunItem) bool { | |
243 | return item.Request.Error == nil | |
244 | } | |
245 | ||
246 | // WithAppendUserAgent will add a string to the user agent prefixed with a | |
247 | // single white space. | |
248 | func WithAppendUserAgent(s string) Option { | |
249 | return func(r *Request) { | |
250 | r.Handlers.Build.PushBack(func(r2 *Request) { | |
251 | AddToUserAgent(r, s) | |
252 | }) | |
253 | } | |
254 | } | |
255 | ||
256 | // MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request | |
257 | // header. If the extra parameters are provided they will be added as metadata to the | |
258 | // name/version pair resulting in the following format. | |
259 | // "name/version (extra0; extra1; ...)" | |
260 | // The user agent part will be concatenated with this current request's user agent string. | |
261 | func MakeAddToUserAgentHandler(name, version string, extra ...string) func(*Request) { | |
262 | ua := fmt.Sprintf("%s/%s", name, version) | |
263 | if len(extra) > 0 { | |
264 | ua += fmt.Sprintf(" (%s)", strings.Join(extra, "; ")) | |
265 | } | |
266 | return func(r *Request) { | |
267 | AddToUserAgent(r, ua) | |
268 | } | |
269 | } | |
270 | ||
271 | // MakeAddToUserAgentFreeFormHandler adds the input to the User-Agent request header. | |
272 | // The input string will be concatenated with the current request's user agent string. | |
273 | func MakeAddToUserAgentFreeFormHandler(s string) func(*Request) { | |
274 | return func(r *Request) { | |
275 | AddToUserAgent(r, s) | |
276 | } | |
277 | } |