Signed-off-by: Antonio Murdaca <me@runcom.ninja>
| 0 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,172 @@ |
| 0 |
+// Package requestdecorator provides helper functions to decorate a request with |
|
| 1 |
+// user agent versions, auth, meta headers. |
|
| 2 |
+package requestdecorator |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "errors" |
|
| 6 |
+ "io" |
|
| 7 |
+ "net/http" |
|
| 8 |
+ "strings" |
|
| 9 |
+ |
|
| 10 |
+ "github.com/Sirupsen/logrus" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+var ( |
|
| 14 |
+ ErrNilRequest = errors.New("request cannot be nil")
|
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+// UAVersionInfo is used to model UserAgent versions. |
|
| 18 |
+type UAVersionInfo struct {
|
|
| 19 |
+ Name string |
|
| 20 |
+ Version string |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func NewUAVersionInfo(name, version string) UAVersionInfo {
|
|
| 24 |
+ return UAVersionInfo{
|
|
| 25 |
+ Name: name, |
|
| 26 |
+ Version: version, |
|
| 27 |
+ } |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func (vi *UAVersionInfo) isValid() bool {
|
|
| 31 |
+ const stopChars = " \t\r\n/" |
|
| 32 |
+ name := vi.Name |
|
| 33 |
+ vers := vi.Version |
|
| 34 |
+ if len(name) == 0 || strings.ContainsAny(name, stopChars) {
|
|
| 35 |
+ return false |
|
| 36 |
+ } |
|
| 37 |
+ if len(vers) == 0 || strings.ContainsAny(vers, stopChars) {
|
|
| 38 |
+ return false |
|
| 39 |
+ } |
|
| 40 |
+ return true |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+// Convert versions to a string and append the string to the string base. |
|
| 44 |
+// |
|
| 45 |
+// Each UAVersionInfo will be converted to a string in the format of |
|
| 46 |
+// "product/version", where the "product" is get from the name field, while |
|
| 47 |
+// version is get from the version field. Several pieces of verson information |
|
| 48 |
+// will be concatinated and separated by space. |
|
| 49 |
+func appendVersions(base string, versions ...UAVersionInfo) string {
|
|
| 50 |
+ if len(versions) == 0 {
|
|
| 51 |
+ return base |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ verstrs := make([]string, 0, 1+len(versions)) |
|
| 55 |
+ if len(base) > 0 {
|
|
| 56 |
+ verstrs = append(verstrs, base) |
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ for _, v := range versions {
|
|
| 60 |
+ if !v.isValid() {
|
|
| 61 |
+ continue |
|
| 62 |
+ } |
|
| 63 |
+ verstrs = append(verstrs, v.Name+"/"+v.Version) |
|
| 64 |
+ } |
|
| 65 |
+ return strings.Join(verstrs, " ") |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// Decorator is used to change an instance of |
|
| 69 |
+// http.Request. It could be used to add more header fields, |
|
| 70 |
+// change body, etc. |
|
| 71 |
+type Decorator interface {
|
|
| 72 |
+ // ChangeRequest() changes the request accordingly. |
|
| 73 |
+ // The changed request will be returned or err will be non-nil |
|
| 74 |
+ // if an error occur. |
|
| 75 |
+ ChangeRequest(req *http.Request) (newReq *http.Request, err error) |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// UserAgentDecorator appends the product/version to the user agent field |
|
| 79 |
+// of a request. |
|
| 80 |
+type UserAgentDecorator struct {
|
|
| 81 |
+ Versions []UAVersionInfo |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+func (h *UserAgentDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
|
| 85 |
+ if req == nil {
|
|
| 86 |
+ return req, ErrNilRequest |
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ userAgent := appendVersions(req.UserAgent(), h.Versions...) |
|
| 90 |
+ if len(userAgent) > 0 {
|
|
| 91 |
+ req.Header.Set("User-Agent", userAgent)
|
|
| 92 |
+ } |
|
| 93 |
+ return req, nil |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+type MetaHeadersDecorator struct {
|
|
| 97 |
+ Headers map[string][]string |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (h *MetaHeadersDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
|
| 101 |
+ if h.Headers == nil {
|
|
| 102 |
+ return req, ErrNilRequest |
|
| 103 |
+ } |
|
| 104 |
+ for k, v := range h.Headers {
|
|
| 105 |
+ req.Header[k] = v |
|
| 106 |
+ } |
|
| 107 |
+ return req, nil |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+type AuthDecorator struct {
|
|
| 111 |
+ login string |
|
| 112 |
+ password string |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+func NewAuthDecorator(login, password string) Decorator {
|
|
| 116 |
+ return &AuthDecorator{
|
|
| 117 |
+ login: login, |
|
| 118 |
+ password: password, |
|
| 119 |
+ } |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func (self *AuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
|
| 123 |
+ if req == nil {
|
|
| 124 |
+ return req, ErrNilRequest |
|
| 125 |
+ } |
|
| 126 |
+ req.SetBasicAuth(self.login, self.password) |
|
| 127 |
+ return req, nil |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+// RequestFactory creates an HTTP request |
|
| 131 |
+// and applies a list of decorators on the request. |
|
| 132 |
+type RequestFactory struct {
|
|
| 133 |
+ decorators []Decorator |
|
| 134 |
+} |
|
| 135 |
+ |
|
| 136 |
+func NewRequestFactory(d ...Decorator) *RequestFactory {
|
|
| 137 |
+ return &RequestFactory{
|
|
| 138 |
+ decorators: d, |
|
| 139 |
+ } |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+func (f *RequestFactory) AddDecorator(d ...Decorator) {
|
|
| 143 |
+ f.decorators = append(f.decorators, d...) |
|
| 144 |
+} |
|
| 145 |
+ |
|
| 146 |
+func (f *RequestFactory) GetDecorators() []Decorator {
|
|
| 147 |
+ return f.decorators |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+// NewRequest() creates a new *http.Request, |
|
| 151 |
+// applies all decorators in the Factory on the request, |
|
| 152 |
+// then applies decorators provided by d on the request. |
|
| 153 |
+func (h *RequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...Decorator) (*http.Request, error) {
|
|
| 154 |
+ req, err := http.NewRequest(method, urlStr, body) |
|
| 155 |
+ if err != nil {
|
|
| 156 |
+ return nil, err |
|
| 157 |
+ } |
|
| 158 |
+ |
|
| 159 |
+ // By default, a nil factory should work. |
|
| 160 |
+ if h == nil {
|
|
| 161 |
+ return req, nil |
|
| 162 |
+ } |
|
| 163 |
+ for _, dec := range h.decorators {
|
|
| 164 |
+ req, _ = dec.ChangeRequest(req) |
|
| 165 |
+ } |
|
| 166 |
+ for _, dec := range d {
|
|
| 167 |
+ req, _ = dec.ChangeRequest(req) |
|
| 168 |
+ } |
|
| 169 |
+ logrus.Debugf("%v -- HEADERS: %v", req.URL, req.Header)
|
|
| 170 |
+ return req, err |
|
| 171 |
+} |
| 0 | 172 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,222 @@ |
| 0 |
+package requestdecorator |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/http" |
|
| 4 |
+ "strings" |
|
| 5 |
+ "testing" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func TestUAVersionInfo(t *testing.T) {
|
|
| 9 |
+ uavi := NewUAVersionInfo("foo", "bar")
|
|
| 10 |
+ if !uavi.isValid() {
|
|
| 11 |
+ t.Fatalf("UAVersionInfo should be valid")
|
|
| 12 |
+ } |
|
| 13 |
+ uavi = NewUAVersionInfo("", "bar")
|
|
| 14 |
+ if uavi.isValid() {
|
|
| 15 |
+ t.Fatalf("Expected UAVersionInfo to be invalid")
|
|
| 16 |
+ } |
|
| 17 |
+ uavi = NewUAVersionInfo("foo", "")
|
|
| 18 |
+ if uavi.isValid() {
|
|
| 19 |
+ t.Fatalf("Expected UAVersionInfo to be invalid")
|
|
| 20 |
+ } |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func TestUserAgentDecorator(t *testing.T) {
|
|
| 24 |
+ httpVersion := make([]UAVersionInfo, 2) |
|
| 25 |
+ httpVersion = append(httpVersion, NewUAVersionInfo("testname", "testversion"))
|
|
| 26 |
+ httpVersion = append(httpVersion, NewUAVersionInfo("name", "version"))
|
|
| 27 |
+ uad := &UserAgentDecorator{
|
|
| 28 |
+ Versions: httpVersion, |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
|
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ t.Fatal(err) |
|
| 34 |
+ } |
|
| 35 |
+ reqDecorated, err := uad.ChangeRequest(req) |
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ t.Fatal(err) |
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 40 |
+ if reqDecorated.Header.Get("User-Agent") != "testname/testversion name/version" {
|
|
| 41 |
+ t.Fatalf("Request should have User-Agent 'testname/testversion name/version'")
|
|
| 42 |
+ } |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+func TestUserAgentDecoratorErr(t *testing.T) {
|
|
| 46 |
+ httpVersion := make([]UAVersionInfo, 0) |
|
| 47 |
+ uad := &UserAgentDecorator{
|
|
| 48 |
+ Versions: httpVersion, |
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+ var req *http.Request |
|
| 52 |
+ _, err := uad.ChangeRequest(req) |
|
| 53 |
+ if err == nil {
|
|
| 54 |
+ t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
|
|
| 55 |
+ } |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func TestMetaHeadersDecorator(t *testing.T) {
|
|
| 59 |
+ var headers = map[string][]string{
|
|
| 60 |
+ "key1": {"value1"},
|
|
| 61 |
+ "key2": {"value2"},
|
|
| 62 |
+ } |
|
| 63 |
+ mhd := &MetaHeadersDecorator{
|
|
| 64 |
+ Headers: headers, |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
|
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ t.Fatal(err) |
|
| 70 |
+ } |
|
| 71 |
+ reqDecorated, err := mhd.ChangeRequest(req) |
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ t.Fatal(err) |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ v, ok := reqDecorated.Header["key1"] |
|
| 77 |
+ if !ok {
|
|
| 78 |
+ t.Fatalf("Expected to have header key1")
|
|
| 79 |
+ } |
|
| 80 |
+ if v[0] != "value1" {
|
|
| 81 |
+ t.Fatalf("Expected value for key1 isn't value1")
|
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ v, ok = reqDecorated.Header["key2"] |
|
| 85 |
+ if !ok {
|
|
| 86 |
+ t.Fatalf("Expected to have header key2")
|
|
| 87 |
+ } |
|
| 88 |
+ if v[0] != "value2" {
|
|
| 89 |
+ t.Fatalf("Expected value for key2 isn't value2")
|
|
| 90 |
+ } |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+func TestMetaHeadersDecoratorErr(t *testing.T) {
|
|
| 94 |
+ mhd := &MetaHeadersDecorator{}
|
|
| 95 |
+ |
|
| 96 |
+ var req *http.Request |
|
| 97 |
+ _, err := mhd.ChangeRequest(req) |
|
| 98 |
+ if err == nil {
|
|
| 99 |
+ t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
|
|
| 100 |
+ } |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func TestAuthDecorator(t *testing.T) {
|
|
| 104 |
+ ad := NewAuthDecorator("test", "password")
|
|
| 105 |
+ |
|
| 106 |
+ req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
|
|
| 107 |
+ if err != nil {
|
|
| 108 |
+ t.Fatal(err) |
|
| 109 |
+ } |
|
| 110 |
+ reqDecorated, err := ad.ChangeRequest(req) |
|
| 111 |
+ if err != nil {
|
|
| 112 |
+ t.Fatal(err) |
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ username, password, ok := reqDecorated.BasicAuth() |
|
| 116 |
+ if !ok {
|
|
| 117 |
+ t.Fatalf("Cannot retrieve basic auth info from request")
|
|
| 118 |
+ } |
|
| 119 |
+ if username != "test" {
|
|
| 120 |
+ t.Fatalf("Expected username to be test, got %s", username)
|
|
| 121 |
+ } |
|
| 122 |
+ if password != "password" {
|
|
| 123 |
+ t.Fatalf("Expected password to be password, got %s", password)
|
|
| 124 |
+ } |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+func TestAuthDecoratorErr(t *testing.T) {
|
|
| 128 |
+ ad := &AuthDecorator{}
|
|
| 129 |
+ |
|
| 130 |
+ var req *http.Request |
|
| 131 |
+ _, err := ad.ChangeRequest(req) |
|
| 132 |
+ if err == nil {
|
|
| 133 |
+ t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
|
|
| 134 |
+ } |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+func TestRequestFactory(t *testing.T) {
|
|
| 138 |
+ ad := NewAuthDecorator("test", "password")
|
|
| 139 |
+ httpVersion := make([]UAVersionInfo, 2) |
|
| 140 |
+ httpVersion = append(httpVersion, NewUAVersionInfo("testname", "testversion"))
|
|
| 141 |
+ httpVersion = append(httpVersion, NewUAVersionInfo("name", "version"))
|
|
| 142 |
+ uad := &UserAgentDecorator{
|
|
| 143 |
+ Versions: httpVersion, |
|
| 144 |
+ } |
|
| 145 |
+ |
|
| 146 |
+ requestFactory := NewRequestFactory(ad, uad) |
|
| 147 |
+ |
|
| 148 |
+ if dlen := requestFactory.GetDecorators(); len(dlen) != 2 {
|
|
| 149 |
+ t.Fatalf("Expected to have two decorators, got %d", dlen)
|
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ req, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"))
|
|
| 153 |
+ if err != nil {
|
|
| 154 |
+ t.Fatal(err) |
|
| 155 |
+ } |
|
| 156 |
+ |
|
| 157 |
+ username, password, ok := req.BasicAuth() |
|
| 158 |
+ if !ok {
|
|
| 159 |
+ t.Fatalf("Cannot retrieve basic auth info from request")
|
|
| 160 |
+ } |
|
| 161 |
+ if username != "test" {
|
|
| 162 |
+ t.Fatalf("Expected username to be test, got %s", username)
|
|
| 163 |
+ } |
|
| 164 |
+ if password != "password" {
|
|
| 165 |
+ t.Fatalf("Expected password to be password, got %s", password)
|
|
| 166 |
+ } |
|
| 167 |
+ if req.Header.Get("User-Agent") != "testname/testversion name/version" {
|
|
| 168 |
+ t.Fatalf("Request should have User-Agent 'testname/testversion name/version'")
|
|
| 169 |
+ } |
|
| 170 |
+} |
|
| 171 |
+ |
|
| 172 |
+func TestRequestFactoryNewRequestWithDecorators(t *testing.T) {
|
|
| 173 |
+ ad := NewAuthDecorator("test", "password")
|
|
| 174 |
+ |
|
| 175 |
+ requestFactory := NewRequestFactory(ad) |
|
| 176 |
+ |
|
| 177 |
+ if dlen := requestFactory.GetDecorators(); len(dlen) != 1 {
|
|
| 178 |
+ t.Fatalf("Expected to have one decorators, got %d", dlen)
|
|
| 179 |
+ } |
|
| 180 |
+ |
|
| 181 |
+ ad2 := NewAuthDecorator("test2", "password2")
|
|
| 182 |
+ |
|
| 183 |
+ req, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"), ad2)
|
|
| 184 |
+ if err != nil {
|
|
| 185 |
+ t.Fatal(err) |
|
| 186 |
+ } |
|
| 187 |
+ |
|
| 188 |
+ username, password, ok := req.BasicAuth() |
|
| 189 |
+ if !ok {
|
|
| 190 |
+ t.Fatalf("Cannot retrieve basic auth info from request")
|
|
| 191 |
+ } |
|
| 192 |
+ if username != "test2" {
|
|
| 193 |
+ t.Fatalf("Expected username to be test, got %s", username)
|
|
| 194 |
+ } |
|
| 195 |
+ if password != "password2" {
|
|
| 196 |
+ t.Fatalf("Expected password to be password, got %s", password)
|
|
| 197 |
+ } |
|
| 198 |
+} |
|
| 199 |
+ |
|
| 200 |
+func TestRequestFactoryAddDecorator(t *testing.T) {
|
|
| 201 |
+ requestFactory := NewRequestFactory() |
|
| 202 |
+ |
|
| 203 |
+ if dlen := requestFactory.GetDecorators(); len(dlen) != 0 {
|
|
| 204 |
+ t.Fatalf("Expected to have zero decorators, got %d", dlen)
|
|
| 205 |
+ } |
|
| 206 |
+ |
|
| 207 |
+ ad := NewAuthDecorator("test", "password")
|
|
| 208 |
+ requestFactory.AddDecorator(ad) |
|
| 209 |
+ |
|
| 210 |
+ if dlen := requestFactory.GetDecorators(); len(dlen) != 1 {
|
|
| 211 |
+ t.Fatalf("Expected to have one decorators, got %d", dlen)
|
|
| 212 |
+ } |
|
| 213 |
+} |
|
| 214 |
+ |
|
| 215 |
+func TestRequestFactoryNil(t *testing.T) {
|
|
| 216 |
+ var requestFactory RequestFactory |
|
| 217 |
+ _, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"))
|
|
| 218 |
+ if err != nil {
|
|
| 219 |
+ t.Fatalf("Expected not to get and error, got %s", err)
|
|
| 220 |
+ } |
|
| 221 |
+} |
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
"time" |
| 15 | 15 |
|
| 16 | 16 |
"github.com/Sirupsen/logrus" |
| 17 |
- "github.com/docker/docker/utils" |
|
| 17 |
+ "github.com/docker/docker/pkg/requestdecorator" |
|
| 18 | 18 |
) |
| 19 | 19 |
|
| 20 | 20 |
const ( |
| ... | ... |
@@ -225,7 +225,7 @@ func SaveConfig(configFile *ConfigFile) error {
|
| 225 | 225 |
} |
| 226 | 226 |
|
| 227 | 227 |
// Login tries to register/login to the registry server. |
| 228 |
-func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
|
|
| 228 |
+func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
|
|
| 229 | 229 |
// Separates the v2 registry login logic from the v1 logic. |
| 230 | 230 |
if registryEndpoint.Version == APIVersion2 {
|
| 231 | 231 |
return loginV2(authConfig, registryEndpoint, factory) |
| ... | ... |
@@ -235,7 +235,7 @@ func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HT |
| 235 | 235 |
} |
| 236 | 236 |
|
| 237 | 237 |
// loginV1 tries to register/login to the v1 registry server. |
| 238 |
-func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
|
|
| 238 |
+func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
|
|
| 239 | 239 |
var ( |
| 240 | 240 |
status string |
| 241 | 241 |
reqBody []byte |
| ... | ... |
@@ -348,7 +348,7 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils. |
| 348 | 348 |
// now, users should create their account through other means like directly from a web page |
| 349 | 349 |
// served by the v2 registry service provider. Whether this will be supported in the future |
| 350 | 350 |
// is to be determined. |
| 351 |
-func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
|
|
| 351 |
+func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
|
|
| 352 | 352 |
logrus.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
|
| 353 | 353 |
var ( |
| 354 | 354 |
err error |
| ... | ... |
@@ -381,7 +381,7 @@ func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils. |
| 381 | 381 |
return "", fmt.Errorf("no successful auth challenge for %s - errors: %s", registryEndpoint, allErrors)
|
| 382 | 382 |
} |
| 383 | 383 |
|
| 384 |
-func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *utils.HTTPRequestFactory) error {
|
|
| 384 |
+func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
|
|
| 385 | 385 |
req, err := factory.NewRequest("GET", registryEndpoint.Path(""), nil)
|
| 386 | 386 |
if err != nil {
|
| 387 | 387 |
return err |
| ... | ... |
@@ -402,7 +402,7 @@ func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, regis |
| 402 | 402 |
return nil |
| 403 | 403 |
} |
| 404 | 404 |
|
| 405 |
-func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *utils.HTTPRequestFactory) error {
|
|
| 405 |
+func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
|
|
| 406 | 406 |
token, err := getToken(authConfig.Username, authConfig.Password, params, registryEndpoint, client, factory) |
| 407 | 407 |
if err != nil {
|
| 408 | 408 |
return err |
| ... | ... |
@@ -11,8 +11,8 @@ import ( |
| 11 | 11 |
"strings" |
| 12 | 12 |
|
| 13 | 13 |
"github.com/Sirupsen/logrus" |
| 14 |
+ "github.com/docker/docker/pkg/requestdecorator" |
|
| 14 | 15 |
"github.com/docker/docker/registry/v2" |
| 15 |
- "github.com/docker/docker/utils" |
|
| 16 | 16 |
) |
| 17 | 17 |
|
| 18 | 18 |
// for mocking in unit tests |
| ... | ... |
@@ -162,7 +162,7 @@ func (e *Endpoint) Ping() (RegistryInfo, error) {
|
| 162 | 162 |
return RegistryInfo{}, fmt.Errorf("unable to ping registry endpoint %s\nv2 ping attempt failed with error: %s\n v1 ping attempt failed with error: %s", e, errV2, errV1)
|
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 |
-func (e *Endpoint) pingV1(factory *utils.HTTPRequestFactory) (RegistryInfo, error) {
|
|
| 165 |
+func (e *Endpoint) pingV1(factory *requestdecorator.RequestFactory) (RegistryInfo, error) {
|
|
| 166 | 166 |
logrus.Debugf("attempting v1 ping for registry endpoint %s", e)
|
| 167 | 167 |
|
| 168 | 168 |
if e.String() == IndexServerAddress() {
|
| ... | ... |
@@ -216,7 +216,7 @@ func (e *Endpoint) pingV1(factory *utils.HTTPRequestFactory) (RegistryInfo, erro |
| 216 | 216 |
return info, nil |
| 217 | 217 |
} |
| 218 | 218 |
|
| 219 |
-func (e *Endpoint) pingV2(factory *utils.HTTPRequestFactory) (RegistryInfo, error) {
|
|
| 219 |
+func (e *Endpoint) pingV2(factory *requestdecorator.RequestFactory) (RegistryInfo, error) {
|
|
| 220 | 220 |
logrus.Debugf("attempting v2 ping for registry endpoint %s", e)
|
| 221 | 221 |
|
| 222 | 222 |
req, err := factory.NewRequest("GET", e.Path(""), nil)
|
| ... | ... |
@@ -5,42 +5,26 @@ import ( |
| 5 | 5 |
|
| 6 | 6 |
"github.com/docker/docker/autogen/dockerversion" |
| 7 | 7 |
"github.com/docker/docker/pkg/parsers/kernel" |
| 8 |
- "github.com/docker/docker/utils" |
|
| 8 |
+ "github.com/docker/docker/pkg/requestdecorator" |
|
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
-func HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFactory {
|
|
| 11 |
+func HTTPRequestFactory(metaHeaders map[string][]string) *requestdecorator.RequestFactory {
|
|
| 12 | 12 |
// FIXME: this replicates the 'info' job. |
| 13 |
- httpVersion := make([]utils.VersionInfo, 0, 4) |
|
| 14 |
- httpVersion = append(httpVersion, &simpleVersionInfo{"docker", dockerversion.VERSION})
|
|
| 15 |
- httpVersion = append(httpVersion, &simpleVersionInfo{"go", runtime.Version()})
|
|
| 16 |
- httpVersion = append(httpVersion, &simpleVersionInfo{"git-commit", dockerversion.GITCOMMIT})
|
|
| 13 |
+ httpVersion := make([]requestdecorator.UAVersionInfo, 0, 4) |
|
| 14 |
+ httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("docker", dockerversion.VERSION))
|
|
| 15 |
+ httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("go", runtime.Version()))
|
|
| 16 |
+ httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("git-commit", dockerversion.GITCOMMIT))
|
|
| 17 | 17 |
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
| 18 |
- httpVersion = append(httpVersion, &simpleVersionInfo{"kernel", kernelVersion.String()})
|
|
| 18 |
+ httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("kernel", kernelVersion.String()))
|
|
| 19 | 19 |
} |
| 20 |
- httpVersion = append(httpVersion, &simpleVersionInfo{"os", runtime.GOOS})
|
|
| 21 |
- httpVersion = append(httpVersion, &simpleVersionInfo{"arch", runtime.GOARCH})
|
|
| 22 |
- ud := utils.NewHTTPUserAgentDecorator(httpVersion...) |
|
| 23 |
- md := &utils.HTTPMetaHeadersDecorator{
|
|
| 20 |
+ httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("os", runtime.GOOS))
|
|
| 21 |
+ httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("arch", runtime.GOARCH))
|
|
| 22 |
+ uad := &requestdecorator.UserAgentDecorator{
|
|
| 23 |
+ Versions: httpVersion, |
|
| 24 |
+ } |
|
| 25 |
+ mhd := &requestdecorator.MetaHeadersDecorator{
|
|
| 24 | 26 |
Headers: metaHeaders, |
| 25 | 27 |
} |
| 26 |
- factory := utils.NewHTTPRequestFactory(ud, md) |
|
| 28 |
+ factory := requestdecorator.NewRequestFactory(uad, mhd) |
|
| 27 | 29 |
return factory |
| 28 | 30 |
} |
| 29 |
- |
|
| 30 |
-// simpleVersionInfo is a simple implementation of |
|
| 31 |
-// the interface VersionInfo, which is used |
|
| 32 |
-// to provide version information for some product, |
|
| 33 |
-// component, etc. It stores the product name and the version |
|
| 34 |
-// in string and returns them on calls to Name() and Version(). |
|
| 35 |
-type simpleVersionInfo struct {
|
|
| 36 |
- name string |
|
| 37 |
- version string |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (v *simpleVersionInfo) Name() string {
|
|
| 41 |
- return v.name |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-func (v *simpleVersionInfo) Version() string {
|
|
| 45 |
- return v.version |
|
| 46 |
-} |
| ... | ... |
@@ -7,7 +7,7 @@ import ( |
| 7 | 7 |
"strings" |
| 8 | 8 |
"testing" |
| 9 | 9 |
|
| 10 |
- "github.com/docker/docker/utils" |
|
| 10 |
+ "github.com/docker/docker/pkg/requestdecorator" |
|
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 | 13 |
var ( |
| ... | ... |
@@ -25,7 +25,7 @@ func spawnTestRegistrySession(t *testing.T) *Session {
|
| 25 | 25 |
if err != nil {
|
| 26 | 26 |
t.Fatal(err) |
| 27 | 27 |
} |
| 28 |
- r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), endpoint, true) |
|
| 28 |
+ r, err := NewSession(authConfig, requestdecorator.NewRequestFactory(), endpoint, true) |
|
| 29 | 29 |
if err != nil {
|
| 30 | 30 |
t.Fatal(err) |
| 31 | 31 |
} |
| ... | ... |
@@ -40,7 +40,7 @@ func TestPublicSession(t *testing.T) {
|
| 40 | 40 |
if err != nil {
|
| 41 | 41 |
t.Fatal(err) |
| 42 | 42 |
} |
| 43 |
- r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), endpoint, true) |
|
| 43 |
+ r, err := NewSession(authConfig, requestdecorator.NewRequestFactory(), endpoint, true) |
|
| 44 | 44 |
if err != nil {
|
| 45 | 45 |
t.Fatal(err) |
| 46 | 46 |
} |
| ... | ... |
@@ -19,19 +19,20 @@ import ( |
| 19 | 19 |
|
| 20 | 20 |
"github.com/Sirupsen/logrus" |
| 21 | 21 |
"github.com/docker/docker/pkg/httputils" |
| 22 |
+ "github.com/docker/docker/pkg/requestdecorator" |
|
| 22 | 23 |
"github.com/docker/docker/pkg/tarsum" |
| 23 | 24 |
"github.com/docker/docker/utils" |
| 24 | 25 |
) |
| 25 | 26 |
|
| 26 | 27 |
type Session struct {
|
| 27 | 28 |
authConfig *AuthConfig |
| 28 |
- reqFactory *utils.HTTPRequestFactory |
|
| 29 |
+ reqFactory *requestdecorator.RequestFactory |
|
| 29 | 30 |
indexEndpoint *Endpoint |
| 30 | 31 |
jar *cookiejar.Jar |
| 31 | 32 |
timeout TimeoutType |
| 32 | 33 |
} |
| 33 | 34 |
|
| 34 |
-func NewSession(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
|
|
| 35 |
+func NewSession(authConfig *AuthConfig, factory *requestdecorator.RequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
|
|
| 35 | 36 |
r = &Session{
|
| 36 | 37 |
authConfig: authConfig, |
| 37 | 38 |
indexEndpoint: endpoint, |
| ... | ... |
@@ -55,7 +56,7 @@ func NewSession(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, endpo |
| 55 | 55 |
} |
| 56 | 56 |
if info.Standalone {
|
| 57 | 57 |
logrus.Debugf("Endpoint %s is eligible for private registry. Enabling decorator.", r.indexEndpoint.String())
|
| 58 |
- dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password) |
|
| 58 |
+ dec := requestdecorator.NewAuthDecorator(authConfig.Username, authConfig.Password) |
|
| 59 | 59 |
factory.AddDecorator(dec) |
| 60 | 60 |
} |
| 61 | 61 |
} |
| ... | ... |
@@ -8,14 +8,14 @@ import ( |
| 8 | 8 |
"net/url" |
| 9 | 9 |
"strings" |
| 10 | 10 |
|
| 11 |
- "github.com/docker/docker/utils" |
|
| 11 |
+ "github.com/docker/docker/pkg/requestdecorator" |
|
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 | 14 |
type tokenResponse struct {
|
| 15 | 15 |
Token string `json:"token"` |
| 16 | 16 |
} |
| 17 | 17 |
|
| 18 |
-func getToken(username, password string, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *utils.HTTPRequestFactory) (token string, err error) {
|
|
| 18 |
+func getToken(username, password string, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) (token string, err error) {
|
|
| 19 | 19 |
realm, ok := params["realm"] |
| 20 | 20 |
if !ok {
|
| 21 | 21 |
return "", errors.New("no realm specified for token auth challenge")
|
| 22 | 22 |
deleted file mode 100644 |
| ... | ... |
@@ -1,168 +0,0 @@ |
| 1 |
-package utils |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "io" |
|
| 5 |
- "net/http" |
|
| 6 |
- "strings" |
|
| 7 |
- |
|
| 8 |
- "github.com/Sirupsen/logrus" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-// VersionInfo is used to model entities which has a version. |
|
| 12 |
-// It is basically a tupple with name and version. |
|
| 13 |
-type VersionInfo interface {
|
|
| 14 |
- Name() string |
|
| 15 |
- Version() string |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-func validVersion(version VersionInfo) bool {
|
|
| 19 |
- const stopChars = " \t\r\n/" |
|
| 20 |
- name := version.Name() |
|
| 21 |
- vers := version.Version() |
|
| 22 |
- if len(name) == 0 || strings.ContainsAny(name, stopChars) {
|
|
| 23 |
- return false |
|
| 24 |
- } |
|
| 25 |
- if len(vers) == 0 || strings.ContainsAny(vers, stopChars) {
|
|
| 26 |
- return false |
|
| 27 |
- } |
|
| 28 |
- return true |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-// Convert versions to a string and append the string to the string base. |
|
| 32 |
-// |
|
| 33 |
-// Each VersionInfo will be converted to a string in the format of |
|
| 34 |
-// "product/version", where the "product" is get from the Name() method, while |
|
| 35 |
-// version is get from the Version() method. Several pieces of verson information |
|
| 36 |
-// will be concatinated and separated by space. |
|
| 37 |
-func appendVersions(base string, versions ...VersionInfo) string {
|
|
| 38 |
- if len(versions) == 0 {
|
|
| 39 |
- return base |
|
| 40 |
- } |
|
| 41 |
- |
|
| 42 |
- verstrs := make([]string, 0, 1+len(versions)) |
|
| 43 |
- if len(base) > 0 {
|
|
| 44 |
- verstrs = append(verstrs, base) |
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- for _, v := range versions {
|
|
| 48 |
- if !validVersion(v) {
|
|
| 49 |
- continue |
|
| 50 |
- } |
|
| 51 |
- verstrs = append(verstrs, v.Name()+"/"+v.Version()) |
|
| 52 |
- } |
|
| 53 |
- return strings.Join(verstrs, " ") |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-// HTTPRequestDecorator is used to change an instance of |
|
| 57 |
-// http.Request. It could be used to add more header fields, |
|
| 58 |
-// change body, etc. |
|
| 59 |
-type HTTPRequestDecorator interface {
|
|
| 60 |
- // ChangeRequest() changes the request accordingly. |
|
| 61 |
- // The changed request will be returned or err will be non-nil |
|
| 62 |
- // if an error occur. |
|
| 63 |
- ChangeRequest(req *http.Request) (newReq *http.Request, err error) |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-// HTTPUserAgentDecorator appends the product/version to the user agent field |
|
| 67 |
-// of a request. |
|
| 68 |
-type HTTPUserAgentDecorator struct {
|
|
| 69 |
- versions []VersionInfo |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-func NewHTTPUserAgentDecorator(versions ...VersionInfo) HTTPRequestDecorator {
|
|
| 73 |
- return &HTTPUserAgentDecorator{
|
|
| 74 |
- versions: versions, |
|
| 75 |
- } |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-func (h *HTTPUserAgentDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
|
|
| 79 |
- if req == nil {
|
|
| 80 |
- return req, nil |
|
| 81 |
- } |
|
| 82 |
- |
|
| 83 |
- userAgent := appendVersions(req.UserAgent(), h.versions...) |
|
| 84 |
- if len(userAgent) > 0 {
|
|
| 85 |
- req.Header.Set("User-Agent", userAgent)
|
|
| 86 |
- } |
|
| 87 |
- return req, nil |
|
| 88 |
-} |
|
| 89 |
- |
|
| 90 |
-type HTTPMetaHeadersDecorator struct {
|
|
| 91 |
- Headers map[string][]string |
|
| 92 |
-} |
|
| 93 |
- |
|
| 94 |
-func (h *HTTPMetaHeadersDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
|
|
| 95 |
- if h.Headers == nil {
|
|
| 96 |
- return req, nil |
|
| 97 |
- } |
|
| 98 |
- for k, v := range h.Headers {
|
|
| 99 |
- req.Header[k] = v |
|
| 100 |
- } |
|
| 101 |
- return req, nil |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-type HTTPAuthDecorator struct {
|
|
| 105 |
- login string |
|
| 106 |
- password string |
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-func NewHTTPAuthDecorator(login, password string) HTTPRequestDecorator {
|
|
| 110 |
- return &HTTPAuthDecorator{
|
|
| 111 |
- login: login, |
|
| 112 |
- password: password, |
|
| 113 |
- } |
|
| 114 |
-} |
|
| 115 |
- |
|
| 116 |
-func (self *HTTPAuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
|
| 117 |
- req.SetBasicAuth(self.login, self.password) |
|
| 118 |
- return req, nil |
|
| 119 |
-} |
|
| 120 |
- |
|
| 121 |
-// HTTPRequestFactory creates an HTTP request |
|
| 122 |
-// and applies a list of decorators on the request. |
|
| 123 |
-type HTTPRequestFactory struct {
|
|
| 124 |
- decorators []HTTPRequestDecorator |
|
| 125 |
-} |
|
| 126 |
- |
|
| 127 |
-func NewHTTPRequestFactory(d ...HTTPRequestDecorator) *HTTPRequestFactory {
|
|
| 128 |
- return &HTTPRequestFactory{
|
|
| 129 |
- decorators: d, |
|
| 130 |
- } |
|
| 131 |
-} |
|
| 132 |
- |
|
| 133 |
-func (self *HTTPRequestFactory) AddDecorator(d ...HTTPRequestDecorator) {
|
|
| 134 |
- self.decorators = append(self.decorators, d...) |
|
| 135 |
-} |
|
| 136 |
- |
|
| 137 |
-func (self *HTTPRequestFactory) GetDecorators() []HTTPRequestDecorator {
|
|
| 138 |
- return self.decorators |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-// NewRequest() creates a new *http.Request, |
|
| 142 |
-// applies all decorators in the HTTPRequestFactory on the request, |
|
| 143 |
-// then applies decorators provided by d on the request. |
|
| 144 |
-func (h *HTTPRequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...HTTPRequestDecorator) (*http.Request, error) {
|
|
| 145 |
- req, err := http.NewRequest(method, urlStr, body) |
|
| 146 |
- if err != nil {
|
|
| 147 |
- return nil, err |
|
| 148 |
- } |
|
| 149 |
- |
|
| 150 |
- // By default, a nil factory should work. |
|
| 151 |
- if h == nil {
|
|
| 152 |
- return req, nil |
|
| 153 |
- } |
|
| 154 |
- for _, dec := range h.decorators {
|
|
| 155 |
- req, err = dec.ChangeRequest(req) |
|
| 156 |
- if err != nil {
|
|
| 157 |
- return nil, err |
|
| 158 |
- } |
|
| 159 |
- } |
|
| 160 |
- for _, dec := range d {
|
|
| 161 |
- req, err = dec.ChangeRequest(req) |
|
| 162 |
- if err != nil {
|
|
| 163 |
- return nil, err |
|
| 164 |
- } |
|
| 165 |
- } |
|
| 166 |
- logrus.Debugf("%v -- HEADERS: %v", req.URL, req.Header)
|
|
| 167 |
- return req, err |
|
| 168 |
-} |