github.com/codemac/docker@v1.2.1-0.20150518222241-6a18412d5b9c/pkg/requestdecorator/requestdecorator.go (about) 1 // Package requestdecorator provides helper functions to decorate a request with 2 // user agent versions, auth, meta headers. 3 package requestdecorator 4 5 import ( 6 "errors" 7 "io" 8 "net/http" 9 "strings" 10 11 "github.com/Sirupsen/logrus" 12 ) 13 14 var ( 15 ErrNilRequest = errors.New("request cannot be nil") 16 ) 17 18 // UAVersionInfo is used to model UserAgent versions. 19 type UAVersionInfo struct { 20 Name string 21 Version string 22 } 23 24 func NewUAVersionInfo(name, version string) UAVersionInfo { 25 return UAVersionInfo{ 26 Name: name, 27 Version: version, 28 } 29 } 30 31 func (vi *UAVersionInfo) isValid() bool { 32 const stopChars = " \t\r\n/" 33 name := vi.Name 34 vers := vi.Version 35 if len(name) == 0 || strings.ContainsAny(name, stopChars) { 36 return false 37 } 38 if len(vers) == 0 || strings.ContainsAny(vers, stopChars) { 39 return false 40 } 41 return true 42 } 43 44 // Convert versions to a string and append the string to the string base. 45 // 46 // Each UAVersionInfo will be converted to a string in the format of 47 // "product/version", where the "product" is get from the name field, while 48 // version is get from the version field. Several pieces of verson information 49 // will be concatinated and separated by space. 50 func appendVersions(base string, versions ...UAVersionInfo) string { 51 if len(versions) == 0 { 52 return base 53 } 54 55 verstrs := make([]string, 0, 1+len(versions)) 56 if len(base) > 0 { 57 verstrs = append(verstrs, base) 58 } 59 60 for _, v := range versions { 61 if !v.isValid() { 62 continue 63 } 64 verstrs = append(verstrs, v.Name+"/"+v.Version) 65 } 66 return strings.Join(verstrs, " ") 67 } 68 69 // Decorator is used to change an instance of 70 // http.Request. It could be used to add more header fields, 71 // change body, etc. 72 type Decorator interface { 73 // ChangeRequest() changes the request accordingly. 74 // The changed request will be returned or err will be non-nil 75 // if an error occur. 76 ChangeRequest(req *http.Request) (newReq *http.Request, err error) 77 } 78 79 // UserAgentDecorator appends the product/version to the user agent field 80 // of a request. 81 type UserAgentDecorator struct { 82 Versions []UAVersionInfo 83 } 84 85 func (h *UserAgentDecorator) ChangeRequest(req *http.Request) (*http.Request, error) { 86 if req == nil { 87 return req, ErrNilRequest 88 } 89 90 userAgent := appendVersions(req.UserAgent(), h.Versions...) 91 if len(userAgent) > 0 { 92 req.Header.Set("User-Agent", userAgent) 93 } 94 return req, nil 95 } 96 97 type MetaHeadersDecorator struct { 98 Headers map[string][]string 99 } 100 101 func (h *MetaHeadersDecorator) ChangeRequest(req *http.Request) (*http.Request, error) { 102 if h.Headers == nil { 103 return req, ErrNilRequest 104 } 105 for k, v := range h.Headers { 106 req.Header[k] = v 107 } 108 return req, nil 109 } 110 111 type AuthDecorator struct { 112 login string 113 password string 114 } 115 116 func NewAuthDecorator(login, password string) Decorator { 117 return &AuthDecorator{ 118 login: login, 119 password: password, 120 } 121 } 122 123 func (self *AuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) { 124 if req == nil { 125 return req, ErrNilRequest 126 } 127 req.SetBasicAuth(self.login, self.password) 128 return req, nil 129 } 130 131 // RequestFactory creates an HTTP request 132 // and applies a list of decorators on the request. 133 type RequestFactory struct { 134 decorators []Decorator 135 } 136 137 func NewRequestFactory(d ...Decorator) *RequestFactory { 138 return &RequestFactory{ 139 decorators: d, 140 } 141 } 142 143 func (f *RequestFactory) AddDecorator(d ...Decorator) { 144 f.decorators = append(f.decorators, d...) 145 } 146 147 func (f *RequestFactory) GetDecorators() []Decorator { 148 return f.decorators 149 } 150 151 // NewRequest() creates a new *http.Request, 152 // applies all decorators in the Factory on the request, 153 // then applies decorators provided by d on the request. 154 func (h *RequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...Decorator) (*http.Request, error) { 155 req, err := http.NewRequest(method, urlStr, body) 156 if err != nil { 157 return nil, err 158 } 159 160 // By default, a nil factory should work. 161 if h == nil { 162 return req, nil 163 } 164 for _, dec := range h.decorators { 165 req, _ = dec.ChangeRequest(req) 166 } 167 for _, dec := range d { 168 req, _ = dec.ChangeRequest(req) 169 } 170 logrus.Debugf("%v -- HEADERS: %v", req.URL, req.Header) 171 return req, err 172 }