8 // A Handlers provides a collection of request handlers for various
9 // stages of handling requests.
10 type Handlers struct {
15 ValidateResponse HandlerList
17 UnmarshalStream HandlerList
18 UnmarshalMeta HandlerList
19 UnmarshalError HandlerList
21 AfterRetry HandlerList
22 CompleteAttempt HandlerList
26 // Copy returns of this handler's lists.
27 func (h *Handlers) Copy() Handlers {
29 Validate: h.Validate.copy(),
30 Build: h.Build.copy(),
33 ValidateResponse: h.ValidateResponse.copy(),
34 Unmarshal: h.Unmarshal.copy(),
35 UnmarshalStream: h.UnmarshalStream.copy(),
36 UnmarshalError: h.UnmarshalError.copy(),
37 UnmarshalMeta: h.UnmarshalMeta.copy(),
38 Retry: h.Retry.copy(),
39 AfterRetry: h.AfterRetry.copy(),
40 CompleteAttempt: h.CompleteAttempt.copy(),
41 Complete: h.Complete.copy(),
45 // Clear removes callback functions for all handlers
46 func (h *Handlers) Clear() {
52 h.UnmarshalStream.Clear()
53 h.UnmarshalMeta.Clear()
54 h.UnmarshalError.Clear()
55 h.ValidateResponse.Clear()
58 h.CompleteAttempt.Clear()
62 // A HandlerListRunItem represents an entry in the HandlerList which
64 type HandlerListRunItem struct {
70 // A HandlerList manages zero or more handlers in a list.
71 type HandlerList struct {
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.
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
86 // A NamedHandler is a struct that contains a name and function callback.
87 type NamedHandler struct {
92 // copy creates a copy of the handler list.
93 func (l *HandlerList) copy() HandlerList {
95 AfterEachFn: l.AfterEachFn,
101 n.list = append(make([]NamedHandler, 0, len(l.list)), l.list...)
105 // Clear clears the handler list.
106 func (l *HandlerList) Clear() {
110 // Len returns the number of handlers in the list.
111 func (l *HandlerList) Len() int {
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})
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)
125 l.list = append(l.list, n)
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})
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...)
139 // Enough room to prepend into list.
140 l.list = append(l.list, NamedHandler{})
141 copy(l.list[1:], l.list)
146 // Remove removes a NamedHandler n
147 func (l *HandlerList) Remove(n NamedHandler) {
148 l.RemoveByName(n.Name)
151 // RemoveByName removes a NamedHandler by name.
152 func (l *HandlerList) RemoveByName(name string) {
153 for i := 0; i < len(l.list); i++ {
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]
161 // decrement list so next check to length is correct
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 {
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 {
186 for i := 0; i < len(l.list); i++ {
187 if l.list[i].Name == name {
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) {
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
207 func (l *HandlerList) SetFrontNamed(n NamedHandler) {
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 {
217 item := HandlerListRunItem{
218 Index: i, Handler: h, Request: r,
220 if l.AfterEachFn != nil && !l.AfterEachFn(item) {
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 {
233 item.Request.Config.Logger.Log("DEBUG: RequestHandler",
234 item.Index, item.Handler.Name, item.Request.Error)
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
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) {
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)
264 ua += fmt.Sprintf(" (%s)", strings.Join(extra, "; "))
266 return func(r *Request) {
267 AddToUserAgent(r, ua)
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) {