]>
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 | |
17 | UnmarshalMeta HandlerList | |
18 | UnmarshalError HandlerList | |
19 | Retry HandlerList | |
20 | AfterRetry HandlerList | |
21 | Complete HandlerList | |
22 | } | |
23 | ||
24 | // Copy returns of this handler's lists. | |
25 | func (h *Handlers) Copy() Handlers { | |
26 | return Handlers{ | |
27 | Validate: h.Validate.copy(), | |
28 | Build: h.Build.copy(), | |
29 | Sign: h.Sign.copy(), | |
30 | Send: h.Send.copy(), | |
31 | ValidateResponse: h.ValidateResponse.copy(), | |
32 | Unmarshal: h.Unmarshal.copy(), | |
33 | UnmarshalError: h.UnmarshalError.copy(), | |
34 | UnmarshalMeta: h.UnmarshalMeta.copy(), | |
35 | Retry: h.Retry.copy(), | |
36 | AfterRetry: h.AfterRetry.copy(), | |
37 | Complete: h.Complete.copy(), | |
38 | } | |
39 | } | |
40 | ||
41 | // Clear removes callback functions for all handlers | |
42 | func (h *Handlers) Clear() { | |
43 | h.Validate.Clear() | |
44 | h.Build.Clear() | |
45 | h.Send.Clear() | |
46 | h.Sign.Clear() | |
47 | h.Unmarshal.Clear() | |
48 | h.UnmarshalMeta.Clear() | |
49 | h.UnmarshalError.Clear() | |
50 | h.ValidateResponse.Clear() | |
51 | h.Retry.Clear() | |
52 | h.AfterRetry.Clear() | |
53 | h.Complete.Clear() | |
54 | } | |
55 | ||
56 | // A HandlerListRunItem represents an entry in the HandlerList which | |
57 | // is being run. | |
58 | type HandlerListRunItem struct { | |
59 | Index int | |
60 | Handler NamedHandler | |
61 | Request *Request | |
62 | } | |
63 | ||
64 | // A HandlerList manages zero or more handlers in a list. | |
65 | type HandlerList struct { | |
66 | list []NamedHandler | |
67 | ||
68 | // Called after each request handler in the list is called. If set | |
69 | // and the func returns true the HandlerList will continue to iterate | |
70 | // over the request handlers. If false is returned the HandlerList | |
71 | // will stop iterating. | |
72 | // | |
73 | // Should be used if extra logic to be performed between each handler | |
74 | // in the list. This can be used to terminate a list's iteration | |
75 | // based on a condition such as error like, HandlerListStopOnError. | |
76 | // Or for logging like HandlerListLogItem. | |
77 | AfterEachFn func(item HandlerListRunItem) bool | |
78 | } | |
79 | ||
80 | // A NamedHandler is a struct that contains a name and function callback. | |
81 | type NamedHandler struct { | |
82 | Name string | |
83 | Fn func(*Request) | |
84 | } | |
85 | ||
86 | // copy creates a copy of the handler list. | |
87 | func (l *HandlerList) copy() HandlerList { | |
88 | n := HandlerList{ | |
89 | AfterEachFn: l.AfterEachFn, | |
90 | } | |
91 | if len(l.list) == 0 { | |
92 | return n | |
93 | } | |
94 | ||
95 | n.list = append(make([]NamedHandler, 0, len(l.list)), l.list...) | |
96 | return n | |
97 | } | |
98 | ||
99 | // Clear clears the handler list. | |
100 | func (l *HandlerList) Clear() { | |
101 | l.list = l.list[0:0] | |
102 | } | |
103 | ||
104 | // Len returns the number of handlers in the list. | |
105 | func (l *HandlerList) Len() int { | |
106 | return len(l.list) | |
107 | } | |
108 | ||
109 | // PushBack pushes handler f to the back of the handler list. | |
110 | func (l *HandlerList) PushBack(f func(*Request)) { | |
111 | l.PushBackNamed(NamedHandler{"__anonymous", f}) | |
112 | } | |
113 | ||
114 | // PushBackNamed pushes named handler f to the back of the handler list. | |
115 | func (l *HandlerList) PushBackNamed(n NamedHandler) { | |
116 | if cap(l.list) == 0 { | |
117 | l.list = make([]NamedHandler, 0, 5) | |
118 | } | |
119 | l.list = append(l.list, n) | |
120 | } | |
121 | ||
122 | // PushFront pushes handler f to the front of the handler list. | |
123 | func (l *HandlerList) PushFront(f func(*Request)) { | |
124 | l.PushFrontNamed(NamedHandler{"__anonymous", f}) | |
125 | } | |
126 | ||
127 | // PushFrontNamed pushes named handler f to the front of the handler list. | |
128 | func (l *HandlerList) PushFrontNamed(n NamedHandler) { | |
129 | if cap(l.list) == len(l.list) { | |
130 | // Allocating new list required | |
131 | l.list = append([]NamedHandler{n}, l.list...) | |
132 | } else { | |
133 | // Enough room to prepend into list. | |
134 | l.list = append(l.list, NamedHandler{}) | |
135 | copy(l.list[1:], l.list) | |
136 | l.list[0] = n | |
137 | } | |
138 | } | |
139 | ||
140 | // Remove removes a NamedHandler n | |
141 | func (l *HandlerList) Remove(n NamedHandler) { | |
142 | l.RemoveByName(n.Name) | |
143 | } | |
144 | ||
145 | // RemoveByName removes a NamedHandler by name. | |
146 | func (l *HandlerList) RemoveByName(name string) { | |
147 | for i := 0; i < len(l.list); i++ { | |
148 | m := l.list[i] | |
149 | if m.Name == name { | |
150 | // Shift array preventing creating new arrays | |
151 | copy(l.list[i:], l.list[i+1:]) | |
152 | l.list[len(l.list)-1] = NamedHandler{} | |
153 | l.list = l.list[:len(l.list)-1] | |
154 | ||
155 | // decrement list so next check to length is correct | |
156 | i-- | |
157 | } | |
158 | } | |
159 | } | |
160 | ||
161 | // Run executes all handlers in the list with a given request object. | |
162 | func (l *HandlerList) Run(r *Request) { | |
163 | for i, h := range l.list { | |
164 | h.Fn(r) | |
165 | item := HandlerListRunItem{ | |
166 | Index: i, Handler: h, Request: r, | |
167 | } | |
168 | if l.AfterEachFn != nil && !l.AfterEachFn(item) { | |
169 | return | |
170 | } | |
171 | } | |
172 | } | |
173 | ||
174 | // HandlerListLogItem logs the request handler and the state of the | |
175 | // request's Error value. Always returns true to continue iterating | |
176 | // request handlers in a HandlerList. | |
177 | func HandlerListLogItem(item HandlerListRunItem) bool { | |
178 | if item.Request.Config.Logger == nil { | |
179 | return true | |
180 | } | |
181 | item.Request.Config.Logger.Log("DEBUG: RequestHandler", | |
182 | item.Index, item.Handler.Name, item.Request.Error) | |
183 | ||
184 | return true | |
185 | } | |
186 | ||
187 | // HandlerListStopOnError returns false to stop the HandlerList iterating | |
188 | // over request handlers if Request.Error is not nil. True otherwise | |
189 | // to continue iterating. | |
190 | func HandlerListStopOnError(item HandlerListRunItem) bool { | |
191 | return item.Request.Error == nil | |
192 | } | |
193 | ||
194 | // WithAppendUserAgent will add a string to the user agent prefixed with a | |
195 | // single white space. | |
196 | func WithAppendUserAgent(s string) Option { | |
197 | return func(r *Request) { | |
198 | r.Handlers.Build.PushBack(func(r2 *Request) { | |
199 | AddToUserAgent(r, s) | |
200 | }) | |
201 | } | |
202 | } | |
203 | ||
204 | // MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request | |
205 | // header. If the extra parameters are provided they will be added as metadata to the | |
206 | // name/version pair resulting in the following format. | |
207 | // "name/version (extra0; extra1; ...)" | |
208 | // The user agent part will be concatenated with this current request's user agent string. | |
209 | func MakeAddToUserAgentHandler(name, version string, extra ...string) func(*Request) { | |
210 | ua := fmt.Sprintf("%s/%s", name, version) | |
211 | if len(extra) > 0 { | |
212 | ua += fmt.Sprintf(" (%s)", strings.Join(extra, "; ")) | |
213 | } | |
214 | return func(r *Request) { | |
215 | AddToUserAgent(r, ua) | |
216 | } | |
217 | } | |
218 | ||
219 | // MakeAddToUserAgentFreeFormHandler adds the input to the User-Agent request header. | |
220 | // The input string will be concatenated with the current request's user agent string. | |
221 | func MakeAddToUserAgentFreeFormHandler(s string) func(*Request) { | |
222 | return func(r *Request) { | |
223 | AddToUserAgent(r, s) | |
224 | } | |
225 | } |