1package request 2 3import ( 4 "fmt" 5 "strings" 6) 7 8// A Handlers provides a collection of request handlers for various 9// stages of handling requests. 10type Handlers struct { 11 Validate HandlerList 12 Build HandlerList 13 Sign HandlerList 14 Send HandlerList 15 ValidateResponse HandlerList 16 Unmarshal HandlerList 17 UnmarshalStream HandlerList 18 UnmarshalMeta HandlerList 19 UnmarshalError HandlerList 20 Retry HandlerList 21 AfterRetry HandlerList 22 CompleteAttempt HandlerList 23 Complete HandlerList 24} 25 26// Copy returns a copy of this handler's lists. 27func (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(), 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(), 42 } 43} 44 45// Clear removes callback functions for all handlers. 46func (h *Handlers) Clear() { 47 h.Validate.Clear() 48 h.Build.Clear() 49 h.Send.Clear() 50 h.Sign.Clear() 51 h.Unmarshal.Clear() 52 h.UnmarshalStream.Clear() 53 h.UnmarshalMeta.Clear() 54 h.UnmarshalError.Clear() 55 h.ValidateResponse.Clear() 56 h.Retry.Clear() 57 h.AfterRetry.Clear() 58 h.CompleteAttempt.Clear() 59 h.Complete.Clear() 60} 61 62// IsEmpty returns if there are no handlers in any of the handlerlists. 63func (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 107// A HandlerListRunItem represents an entry in the HandlerList which 108// is being run. 109type HandlerListRunItem struct { 110 Index int 111 Handler NamedHandler 112 Request *Request 113} 114 115// A HandlerList manages zero or more handlers in a list. 116type 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. 132type NamedHandler struct { 133 Name string 134 Fn func(*Request) 135} 136 137// copy creates a copy of the handler list. 138func (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. 151func (l *HandlerList) Clear() { 152 l.list = l.list[0:0] 153} 154 155// Len returns the number of handlers in the list. 156func (l *HandlerList) Len() int { 157 return len(l.list) 158} 159 160// PushBack pushes handler f to the back of the handler list. 161func (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. 166func (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. 174func (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. 179func (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 192func (l *HandlerList) Remove(n NamedHandler) { 193 l.RemoveByName(n.Name) 194} 195 196// RemoveByName removes a NamedHandler by name. 197func (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 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. 215func (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 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. 228func (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 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. 243func (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. 252func (l *HandlerList) SetFrontNamed(n NamedHandler) { 253 if !l.SwapNamed(n) { 254 l.PushFrontNamed(n) 255 } 256} 257 258// Run executes all handlers in the list with a given request object. 259func (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. 274func 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. 287func 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. 293func 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. 306func 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. 318func MakeAddToUserAgentFreeFormHandler(s string) func(*Request) { 319 return func(r *Request) { 320 AddToUserAgent(r, s) 321 } 322} 323