Browse code

registry: Refactor requestfactory to use http.RoundTrippers

This patch removes the need for requestFactories and decorators
by implementing http.RoundTripper transports instead.

It refactors some challenging-to-read code.

NewSession now takes an *http.Client that can already have a
custom Transport, it will add its own auth transport by wrapping
it.

The idea is that callers of http.Client should not bother
setting custom headers for every handler but instead it should
be transparent to the callers of a same context.

This patch is needed for future refactorings of registry,
namely refactoring of the v1 client code.

Signed-off-by: Tibor Vass <tibor@docker.com>

Tibor Vass authored on 2015/05/14 23:12:54
Showing 12 changed files
... ...
@@ -60,7 +60,13 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf
60 60
 		return err
61 61
 	}
62 62
 
63
-	r, err := registry.NewSession(imagePullConfig.AuthConfig, registry.HTTPRequestFactory(imagePullConfig.MetaHeaders), endpoint, true)
63
+	// Adds Docker-specific headers as well as user-specified headers (metaHeaders)
64
+	tr := &registry.DockerHeaders{
65
+		registry.NewTransport(registry.ReceiveTimeout, endpoint.IsSecure),
66
+		imagePullConfig.MetaHeaders,
67
+	}
68
+	client := registry.HTTPClient(tr)
69
+	r, err := registry.NewSession(client, imagePullConfig.AuthConfig, endpoint)
64 70
 	if err != nil {
65 71
 		return err
66 72
 	}
... ...
@@ -109,7 +115,7 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *
109 109
 	}
110 110
 
111 111
 	logrus.Debugf("Retrieving the tag list")
112
-	tagsList, err := r.GetRemoteTags(repoData.Endpoints, repoInfo.RemoteName, repoData.Tokens)
112
+	tagsList, err := r.GetRemoteTags(repoData.Endpoints, repoInfo.RemoteName)
113 113
 	if err != nil {
114 114
 		logrus.Errorf("unable to get remote tags: %s", err)
115 115
 		return err
... ...
@@ -240,7 +246,7 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *
240 240
 }
241 241
 
242 242
 func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint string, token []string, sf *streamformatter.StreamFormatter) (bool, error) {
243
-	history, err := r.GetRemoteHistory(imgID, endpoint, token)
243
+	history, err := r.GetRemoteHistory(imgID, endpoint)
244 244
 	if err != nil {
245 245
 		return false, err
246 246
 	}
... ...
@@ -269,7 +275,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
269 269
 			)
270 270
 			retries := 5
271 271
 			for j := 1; j <= retries; j++ {
272
-				imgJSON, imgSize, err = r.GetRemoteImageJSON(id, endpoint, token)
272
+				imgJSON, imgSize, err = r.GetRemoteImageJSON(id, endpoint)
273 273
 				if err != nil && j == retries {
274 274
 					out.Write(sf.FormatProgress(stringid.TruncateID(id), "Error pulling dependent layers", nil))
275 275
 					return layersDownloaded, err
... ...
@@ -297,7 +303,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
297 297
 					status = fmt.Sprintf("Pulling fs layer [retries: %d]", j)
298 298
 				}
299 299
 				out.Write(sf.FormatProgress(stringid.TruncateID(id), status, nil))
300
-				layer, err := r.GetRemoteImageLayer(img.ID, endpoint, token, int64(imgSize))
300
+				layer, err := r.GetRemoteImageLayer(img.ID, endpoint, int64(imgSize))
301 301
 				if uerr, ok := err.(*url.Error); ok {
302 302
 					err = uerr.Err
303 303
 				}
... ...
@@ -141,7 +141,7 @@ func lookupImageOnEndpoint(wg *sync.WaitGroup, r *registry.Session, out io.Write
141 141
 	images chan imagePushData, imagesToPush chan string) {
142 142
 	defer wg.Done()
143 143
 	for image := range images {
144
-		if err := r.LookupRemoteImage(image.id, image.endpoint, image.tokens); err != nil {
144
+		if err := r.LookupRemoteImage(image.id, image.endpoint); err != nil {
145 145
 			logrus.Errorf("Error in LookupRemoteImage: %s", err)
146 146
 			imagesToPush <- image.id
147 147
 			continue
... ...
@@ -199,7 +199,7 @@ func (s *TagStore) pushImageToEndpoint(endpoint string, out io.Writer, remoteNam
199 199
 		}
200 200
 		for _, tag := range tags[id] {
201 201
 			out.Write(sf.FormatStatus("", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(id), endpoint+"repositories/"+remoteName+"/tags/"+tag))
202
-			if err := r.PushRegistryTag(remoteName, id, tag, endpoint, repo.Tokens); err != nil {
202
+			if err := r.PushRegistryTag(remoteName, id, tag, endpoint); err != nil {
203 203
 				return err
204 204
 			}
205 205
 		}
... ...
@@ -258,7 +258,7 @@ func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep strin
258 258
 	}
259 259
 
260 260
 	// Send the json
261
-	if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil {
261
+	if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep); err != nil {
262 262
 		if err == registry.ErrAlreadyExists {
263 263
 			out.Write(sf.FormatProgress(stringid.TruncateID(imgData.ID), "Image already pushed, skipping", nil))
264 264
 			return "", nil
... ...
@@ -284,14 +284,14 @@ func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep strin
284 284
 			NewLines:  false,
285 285
 			ID:        stringid.TruncateID(imgData.ID),
286 286
 			Action:    "Pushing",
287
-		}), ep, token, jsonRaw)
287
+		}), ep, jsonRaw)
288 288
 	if err != nil {
289 289
 		return "", err
290 290
 	}
291 291
 	imgData.Checksum = checksum
292 292
 	imgData.ChecksumPayload = checksumPayload
293 293
 	// Send the checksum
294
-	if err := r.PushImageChecksumRegistry(imgData, ep, token); err != nil {
294
+	if err := r.PushImageChecksumRegistry(imgData, ep); err != nil {
295 295
 		return "", err
296 296
 	}
297 297
 
... ...
@@ -514,7 +514,12 @@ func (s *TagStore) Push(localName string, imagePushConfig *ImagePushConfig) erro
514 514
 		return err
515 515
 	}
516 516
 
517
-	r, err := registry.NewSession(imagePushConfig.AuthConfig, registry.HTTPRequestFactory(imagePushConfig.MetaHeaders), endpoint, false)
517
+	// Adds Docker-specific headers as well as user-specified headers (metaHeaders)
518
+	tr := &registry.DockerHeaders{
519
+		registry.NewTransport(registry.NoTimeout, endpoint.IsSecure),
520
+		imagePushConfig.MetaHeaders,
521
+	}
522
+	r, err := registry.NewSession(client, imagePushConfig.AuthConfig, endpoint)
518 523
 	if err != nil {
519 524
 		return err
520 525
 	}
... ...
@@ -47,7 +47,7 @@ func (vi *UAVersionInfo) isValid() bool {
47 47
 // "product/version", where the "product" is get from the name field, while
48 48
 // version is get from the version field. Several pieces of verson information
49 49
 // will be concatinated and separated by space.
50
-func appendVersions(base string, versions ...UAVersionInfo) string {
50
+func AppendVersions(base string, versions ...UAVersionInfo) string {
51 51
 	if len(versions) == 0 {
52 52
 		return base
53 53
 	}
... ...
@@ -87,7 +87,7 @@ func (h *UserAgentDecorator) ChangeRequest(req *http.Request) (*http.Request, er
87 87
 		return req, ErrNilRequest
88 88
 	}
89 89
 
90
-	userAgent := appendVersions(req.UserAgent(), h.Versions...)
90
+	userAgent := AppendVersions(req.UserAgent(), h.Versions...)
91 91
 	if len(userAgent) > 0 {
92 92
 		req.Header.Set("User-Agent", userAgent)
93 93
 	}
... ...
@@ -11,7 +11,6 @@ import (
11 11
 
12 12
 	"github.com/Sirupsen/logrus"
13 13
 	"github.com/docker/docker/cliconfig"
14
-	"github.com/docker/docker/pkg/requestdecorator"
15 14
 )
16 15
 
17 16
 type RequestAuthorization struct {
... ...
@@ -46,7 +45,6 @@ func (auth *RequestAuthorization) getToken() (string, error) {
46 46
 	}
47 47
 
48 48
 	client := auth.registryEndpoint.HTTPClient()
49
-	factory := HTTPRequestFactory(nil)
50 49
 
51 50
 	for _, challenge := range auth.registryEndpoint.AuthChallenges {
52 51
 		switch strings.ToLower(challenge.Scheme) {
... ...
@@ -59,7 +57,7 @@ func (auth *RequestAuthorization) getToken() (string, error) {
59 59
 				params[k] = v
60 60
 			}
61 61
 			params["scope"] = fmt.Sprintf("%s:%s:%s", auth.resource, auth.scope, strings.Join(auth.actions, ","))
62
-			token, err := getToken(auth.authConfig.Username, auth.authConfig.Password, params, auth.registryEndpoint, client, factory)
62
+			token, err := getToken(auth.authConfig.Username, auth.authConfig.Password, params, auth.registryEndpoint, client)
63 63
 			if err != nil {
64 64
 				return "", err
65 65
 			}
... ...
@@ -92,16 +90,16 @@ func (auth *RequestAuthorization) Authorize(req *http.Request) error {
92 92
 }
93 93
 
94 94
 // Login tries to register/login to the registry server.
95
-func Login(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
95
+func Login(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint) (string, error) {
96 96
 	// Separates the v2 registry login logic from the v1 logic.
97 97
 	if registryEndpoint.Version == APIVersion2 {
98
-		return loginV2(authConfig, registryEndpoint, factory)
98
+		return loginV2(authConfig, registryEndpoint)
99 99
 	}
100
-	return loginV1(authConfig, registryEndpoint, factory)
100
+	return loginV1(authConfig, registryEndpoint)
101 101
 }
102 102
 
103 103
 // loginV1 tries to register/login to the v1 registry server.
104
-func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
104
+func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint) (string, error) {
105 105
 	var (
106 106
 		status        string
107 107
 		reqBody       []byte
... ...
@@ -151,7 +149,7 @@ func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, facto
151 151
 		}
152 152
 	} else if reqStatusCode == 400 {
153 153
 		if string(reqBody) == "\"Username or email already exists\"" {
154
-			req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
154
+			req, err := http.NewRequest("GET", serverAddress+"users/", nil)
155 155
 			req.SetBasicAuth(authConfig.Username, authConfig.Password)
156 156
 			resp, err := client.Do(req)
157 157
 			if err != nil {
... ...
@@ -180,7 +178,7 @@ func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, facto
180 180
 	} else if reqStatusCode == 401 {
181 181
 		// This case would happen with private registries where /v1/users is
182 182
 		// protected, so people can use `docker login` as an auth check.
183
-		req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
183
+		req, err := http.NewRequest("GET", serverAddress+"users/", nil)
184 184
 		req.SetBasicAuth(authConfig.Username, authConfig.Password)
185 185
 		resp, err := client.Do(req)
186 186
 		if err != nil {
... ...
@@ -214,7 +212,7 @@ func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, facto
214 214
 // now, users should create their account through other means like directly from a web page
215 215
 // served by the v2 registry service provider. Whether this will be supported in the future
216 216
 // is to be determined.
217
-func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
217
+func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint) (string, error) {
218 218
 	logrus.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
219 219
 	var (
220 220
 		err       error
... ...
@@ -227,9 +225,9 @@ func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, facto
227 227
 
228 228
 		switch strings.ToLower(challenge.Scheme) {
229 229
 		case "basic":
230
-			err = tryV2BasicAuthLogin(authConfig, challenge.Parameters, registryEndpoint, client, factory)
230
+			err = tryV2BasicAuthLogin(authConfig, challenge.Parameters, registryEndpoint, client)
231 231
 		case "bearer":
232
-			err = tryV2TokenAuthLogin(authConfig, challenge.Parameters, registryEndpoint, client, factory)
232
+			err = tryV2TokenAuthLogin(authConfig, challenge.Parameters, registryEndpoint, client)
233 233
 		default:
234 234
 			// Unsupported challenge types are explicitly skipped.
235 235
 			err = fmt.Errorf("unsupported auth scheme: %q", challenge.Scheme)
... ...
@@ -247,8 +245,8 @@ func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, facto
247 247
 	return "", fmt.Errorf("no successful auth challenge for %s - errors: %s", registryEndpoint, allErrors)
248 248
 }
249 249
 
250
-func tryV2BasicAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
251
-	req, err := factory.NewRequest("GET", registryEndpoint.Path(""), nil)
250
+func tryV2BasicAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client) error {
251
+	req, err := http.NewRequest("GET", registryEndpoint.Path(""), nil)
252 252
 	if err != nil {
253 253
 		return err
254 254
 	}
... ...
@@ -268,13 +266,13 @@ func tryV2BasicAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]str
268 268
 	return nil
269 269
 }
270 270
 
271
-func tryV2TokenAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
272
-	token, err := getToken(authConfig.Username, authConfig.Password, params, registryEndpoint, client, factory)
271
+func tryV2TokenAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client) error {
272
+	token, err := getToken(authConfig.Username, authConfig.Password, params, registryEndpoint, client)
273 273
 	if err != nil {
274 274
 		return err
275 275
 	}
276 276
 
277
-	req, err := factory.NewRequest("GET", registryEndpoint.Path(""), nil)
277
+	req, err := http.NewRequest("GET", registryEndpoint.Path(""), nil)
278 278
 	if err != nil {
279 279
 		return err
280 280
 	}
... ...
@@ -1,7 +1,6 @@
1 1
 package registry
2 2
 
3 3
 import (
4
-	"crypto/tls"
5 4
 	"encoding/json"
6 5
 	"fmt"
7 6
 	"io/ioutil"
... ...
@@ -12,7 +11,6 @@ import (
12 12
 
13 13
 	"github.com/Sirupsen/logrus"
14 14
 	"github.com/docker/distribution/registry/api/v2"
15
-	"github.com/docker/docker/pkg/requestdecorator"
16 15
 )
17 16
 
18 17
 // for mocking in unit tests
... ...
@@ -109,6 +107,7 @@ func (repoInfo *RepositoryInfo) GetEndpoint() (*Endpoint, error) {
109 109
 
110 110
 // Endpoint stores basic information about a registry endpoint.
111 111
 type Endpoint struct {
112
+	client         *http.Client
112 113
 	URL            *url.URL
113 114
 	Version        APIVersion
114 115
 	IsSecure       bool
... ...
@@ -135,25 +134,24 @@ func (e *Endpoint) Path(path string) string {
135 135
 
136 136
 func (e *Endpoint) Ping() (RegistryInfo, error) {
137 137
 	// The ping logic to use is determined by the registry endpoint version.
138
-	factory := HTTPRequestFactory(nil)
139 138
 	switch e.Version {
140 139
 	case APIVersion1:
141
-		return e.pingV1(factory)
140
+		return e.pingV1()
142 141
 	case APIVersion2:
143
-		return e.pingV2(factory)
142
+		return e.pingV2()
144 143
 	}
145 144
 
146 145
 	// APIVersionUnknown
147 146
 	// We should try v2 first...
148 147
 	e.Version = APIVersion2
149
-	regInfo, errV2 := e.pingV2(factory)
148
+	regInfo, errV2 := e.pingV2()
150 149
 	if errV2 == nil {
151 150
 		return regInfo, nil
152 151
 	}
153 152
 
154 153
 	// ... then fallback to v1.
155 154
 	e.Version = APIVersion1
156
-	regInfo, errV1 := e.pingV1(factory)
155
+	regInfo, errV1 := e.pingV1()
157 156
 	if errV1 == nil {
158 157
 		return regInfo, nil
159 158
 	}
... ...
@@ -162,7 +160,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 *requestdecorator.RequestFactory) (RegistryInfo, error) {
165
+func (e *Endpoint) pingV1() (RegistryInfo, error) {
166 166
 	logrus.Debugf("attempting v1 ping for registry endpoint %s", e)
167 167
 
168 168
 	if e.String() == IndexServerAddress() {
... ...
@@ -171,12 +169,12 @@ func (e *Endpoint) pingV1(factory *requestdecorator.RequestFactory) (RegistryInf
171 171
 		return RegistryInfo{Standalone: false}, nil
172 172
 	}
173 173
 
174
-	req, err := factory.NewRequest("GET", e.Path("_ping"), nil)
174
+	req, err := http.NewRequest("GET", e.Path("_ping"), nil)
175 175
 	if err != nil {
176 176
 		return RegistryInfo{Standalone: false}, err
177 177
 	}
178 178
 
179
-	resp, _, err := doRequest(req, nil, ConnectTimeout, e.IsSecure)
179
+	resp, err := e.HTTPClient().Do(req)
180 180
 	if err != nil {
181 181
 		return RegistryInfo{Standalone: false}, err
182 182
 	}
... ...
@@ -216,15 +214,15 @@ func (e *Endpoint) pingV1(factory *requestdecorator.RequestFactory) (RegistryInf
216 216
 	return info, nil
217 217
 }
218 218
 
219
-func (e *Endpoint) pingV2(factory *requestdecorator.RequestFactory) (RegistryInfo, error) {
219
+func (e *Endpoint) pingV2() (RegistryInfo, error) {
220 220
 	logrus.Debugf("attempting v2 ping for registry endpoint %s", e)
221 221
 
222
-	req, err := factory.NewRequest("GET", e.Path(""), nil)
222
+	req, err := http.NewRequest("GET", e.Path(""), nil)
223 223
 	if err != nil {
224 224
 		return RegistryInfo{}, err
225 225
 	}
226 226
 
227
-	resp, _, err := doRequest(req, nil, ConnectTimeout, e.IsSecure)
227
+	resp, err := e.HTTPClient().Do(req)
228 228
 	if err != nil {
229 229
 		return RegistryInfo{}, err
230 230
 	}
... ...
@@ -265,18 +263,9 @@ HeaderLoop:
265 265
 }
266 266
 
267 267
 func (e *Endpoint) HTTPClient() *http.Client {
268
-	tlsConfig := tls.Config{
269
-		MinVersion: tls.VersionTLS10,
270
-	}
271
-	if !e.IsSecure {
272
-		tlsConfig.InsecureSkipVerify = true
273
-	}
274
-	return &http.Client{
275
-		Transport: &http.Transport{
276
-			DisableKeepAlives: true,
277
-			Proxy:             http.ProxyFromEnvironment,
278
-			TLSClientConfig:   &tlsConfig,
279
-		},
280
-		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
268
+	if e.client == nil {
269
+		tr := NewTransport(ConnectTimeout, e.IsSecure)
270
+		e.client = HTTPClient(tr)
281 271
 	}
272
+	return e.client
282 273
 }
283 274
deleted file mode 100644
... ...
@@ -1,30 +0,0 @@
1
-package registry
2
-
3
-import (
4
-	"runtime"
5
-
6
-	"github.com/docker/docker/autogen/dockerversion"
7
-	"github.com/docker/docker/pkg/parsers/kernel"
8
-	"github.com/docker/docker/pkg/requestdecorator"
9
-)
10
-
11
-func HTTPRequestFactory(metaHeaders map[string][]string) *requestdecorator.RequestFactory {
12
-	// FIXME: this replicates the 'info' job.
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
-	if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
18
-		httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("kernel", kernelVersion.String()))
19
-	}
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{
26
-		Headers: metaHeaders,
27
-	}
28
-	factory := requestdecorator.NewRequestFactory(uad, mhd)
29
-	return factory
30
-}
... ...
@@ -8,12 +8,17 @@ import (
8 8
 	"io/ioutil"
9 9
 	"net"
10 10
 	"net/http"
11
+	"net/http/httputil"
11 12
 	"os"
12 13
 	"path"
14
+	"runtime"
13 15
 	"strings"
14 16
 	"time"
15 17
 
16 18
 	"github.com/Sirupsen/logrus"
19
+	"github.com/docker/docker/autogen/dockerversion"
20
+	"github.com/docker/docker/pkg/parsers/kernel"
21
+	"github.com/docker/docker/pkg/requestdecorator"
17 22
 	"github.com/docker/docker/pkg/timeoutconn"
18 23
 )
19 24
 
... ...
@@ -31,66 +36,23 @@ const (
31 31
 	ConnectTimeout
32 32
 )
33 33
 
34
-func newClient(jar http.CookieJar, roots *x509.CertPool, certs []tls.Certificate, timeout TimeoutType, secure bool) *http.Client {
35
-	tlsConfig := tls.Config{
36
-		RootCAs: roots,
37
-		// Avoid fallback to SSL protocols < TLS1.0
38
-		MinVersion:   tls.VersionTLS10,
39
-		Certificates: certs,
40
-	}
41
-
42
-	if !secure {
43
-		tlsConfig.InsecureSkipVerify = true
44
-	}
45
-
46
-	httpTransport := &http.Transport{
47
-		DisableKeepAlives: true,
48
-		Proxy:             http.ProxyFromEnvironment,
49
-		TLSClientConfig:   &tlsConfig,
50
-	}
51
-
52
-	switch timeout {
53
-	case ConnectTimeout:
54
-		httpTransport.Dial = func(proto string, addr string) (net.Conn, error) {
55
-			// Set the connect timeout to 30 seconds to allow for slower connection
56
-			// times...
57
-			d := net.Dialer{Timeout: 30 * time.Second, DualStack: true}
58
-
59
-			conn, err := d.Dial(proto, addr)
60
-			if err != nil {
61
-				return nil, err
62
-			}
63
-			// Set the recv timeout to 10 seconds
64
-			conn.SetDeadline(time.Now().Add(10 * time.Second))
65
-			return conn, nil
66
-		}
67
-	case ReceiveTimeout:
68
-		httpTransport.Dial = func(proto string, addr string) (net.Conn, error) {
69
-			d := net.Dialer{DualStack: true}
70
-
71
-			conn, err := d.Dial(proto, addr)
72
-			if err != nil {
73
-				return nil, err
74
-			}
75
-			conn = timeoutconn.New(conn, 1*time.Minute)
76
-			return conn, nil
77
-		}
78
-	}
79
-
80
-	return &http.Client{
81
-		Transport:     httpTransport,
82
-		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
83
-		Jar:           jar,
84
-	}
34
+type httpsTransport struct {
35
+	*http.Transport
85 36
 }
86 37
 
87
-func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType, secure bool) (*http.Response, *http.Client, error) {
38
+// DRAGONS(tiborvass): If someone wonders why do we set tlsconfig in a roundtrip,
39
+// it's because it's so as to match the current behavior in master: we generate the
40
+// certpool on every-goddam-request. It's not great, but it allows people to just put
41
+// the certs in /etc/docker/certs.d/.../ and let docker "pick it up" immediately. Would
42
+// prefer an fsnotify implementation, but that was out of scope of my refactoring.
43
+// TODO: improve things
44
+func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
88 45
 	var (
89
-		pool  *x509.CertPool
46
+		roots *x509.CertPool
90 47
 		certs []tls.Certificate
91 48
 	)
92 49
 
93
-	if secure && req.URL.Scheme == "https" {
50
+	if req.URL.Scheme == "https" {
94 51
 		hasFile := func(files []os.FileInfo, name string) bool {
95 52
 			for _, f := range files {
96 53
 				if f.Name() == name {
... ...
@@ -104,31 +66,31 @@ func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType, secur
104 104
 		logrus.Debugf("hostDir: %s", hostDir)
105 105
 		fs, err := ioutil.ReadDir(hostDir)
106 106
 		if err != nil && !os.IsNotExist(err) {
107
-			return nil, nil, err
107
+			return nil, err
108 108
 		}
109 109
 
110 110
 		for _, f := range fs {
111 111
 			if strings.HasSuffix(f.Name(), ".crt") {
112
-				if pool == nil {
113
-					pool = x509.NewCertPool()
112
+				if roots == nil {
113
+					roots = x509.NewCertPool()
114 114
 				}
115 115
 				logrus.Debugf("crt: %s", hostDir+"/"+f.Name())
116 116
 				data, err := ioutil.ReadFile(path.Join(hostDir, f.Name()))
117 117
 				if err != nil {
118
-					return nil, nil, err
118
+					return nil, err
119 119
 				}
120
-				pool.AppendCertsFromPEM(data)
120
+				roots.AppendCertsFromPEM(data)
121 121
 			}
122 122
 			if strings.HasSuffix(f.Name(), ".cert") {
123 123
 				certName := f.Name()
124 124
 				keyName := certName[:len(certName)-5] + ".key"
125 125
 				logrus.Debugf("cert: %s", hostDir+"/"+f.Name())
126 126
 				if !hasFile(fs, keyName) {
127
-					return nil, nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
127
+					return nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
128 128
 				}
129 129
 				cert, err := tls.LoadX509KeyPair(path.Join(hostDir, certName), path.Join(hostDir, keyName))
130 130
 				if err != nil {
131
-					return nil, nil, err
131
+					return nil, err
132 132
 				}
133 133
 				certs = append(certs, cert)
134 134
 			}
... ...
@@ -137,24 +99,142 @@ func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType, secur
137 137
 				certName := keyName[:len(keyName)-4] + ".cert"
138 138
 				logrus.Debugf("key: %s", hostDir+"/"+f.Name())
139 139
 				if !hasFile(fs, certName) {
140
-					return nil, nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
140
+					return nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
141 141
 				}
142 142
 			}
143 143
 		}
144
+		if tr.Transport.TLSClientConfig == nil {
145
+			tr.Transport.TLSClientConfig = &tls.Config{
146
+				// Avoid fallback to SSL protocols < TLS1.0
147
+				MinVersion: tls.VersionTLS10,
148
+			}
149
+		}
150
+		tr.Transport.TLSClientConfig.RootCAs = roots
151
+		tr.Transport.TLSClientConfig.Certificates = certs
152
+	}
153
+	return tr.Transport.RoundTrip(req)
154
+}
155
+
156
+func NewTransport(timeout TimeoutType, secure bool) http.RoundTripper {
157
+	tlsConfig := tls.Config{
158
+		// Avoid fallback to SSL protocols < TLS1.0
159
+		MinVersion:         tls.VersionTLS10,
160
+		InsecureSkipVerify: !secure,
161
+	}
162
+
163
+	transport := &http.Transport{
164
+		DisableKeepAlives: true,
165
+		Proxy:             http.ProxyFromEnvironment,
166
+		TLSClientConfig:   &tlsConfig,
144 167
 	}
145 168
 
146
-	if len(certs) == 0 {
147
-		client := newClient(jar, pool, nil, timeout, secure)
148
-		res, err := client.Do(req)
149
-		if err != nil {
150
-			return nil, nil, err
169
+	switch timeout {
170
+	case ConnectTimeout:
171
+		transport.Dial = func(proto string, addr string) (net.Conn, error) {
172
+			// Set the connect timeout to 30 seconds to allow for slower connection
173
+			// times...
174
+			d := net.Dialer{Timeout: 30 * time.Second, DualStack: true}
175
+
176
+			conn, err := d.Dial(proto, addr)
177
+			if err != nil {
178
+				return nil, err
179
+			}
180
+			// Set the recv timeout to 10 seconds
181
+			conn.SetDeadline(time.Now().Add(10 * time.Second))
182
+			return conn, nil
183
+		}
184
+	case ReceiveTimeout:
185
+		transport.Dial = func(proto string, addr string) (net.Conn, error) {
186
+			d := net.Dialer{DualStack: true}
187
+
188
+			conn, err := d.Dial(proto, addr)
189
+			if err != nil {
190
+				return nil, err
191
+			}
192
+			conn = timeoutconn.New(conn, 1*time.Minute)
193
+			return conn, nil
151 194
 		}
152
-		return res, client, nil
153 195
 	}
154 196
 
155
-	client := newClient(jar, pool, certs, timeout, secure)
156
-	res, err := client.Do(req)
157
-	return res, client, err
197
+	if secure {
198
+		// note: httpsTransport also handles http transport
199
+		// but for HTTPS, it sets up the certs
200
+		return &httpsTransport{transport}
201
+	}
202
+
203
+	return transport
204
+}
205
+
206
+type DockerHeaders struct {
207
+	http.RoundTripper
208
+	Headers http.Header
209
+}
210
+
211
+// cloneRequest returns a clone of the provided *http.Request.
212
+// The clone is a shallow copy of the struct and its Header map
213
+func cloneRequest(r *http.Request) *http.Request {
214
+	// shallow copy of the struct
215
+	r2 := new(http.Request)
216
+	*r2 = *r
217
+	// deep copy of the Header
218
+	r2.Header = make(http.Header, len(r.Header))
219
+	for k, s := range r.Header {
220
+		r2.Header[k] = append([]string(nil), s...)
221
+	}
222
+	return r2
223
+}
224
+
225
+func (tr *DockerHeaders) RoundTrip(req *http.Request) (*http.Response, error) {
226
+	req = cloneRequest(req)
227
+	httpVersion := make([]requestdecorator.UAVersionInfo, 0, 4)
228
+	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("docker", dockerversion.VERSION))
229
+	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("go", runtime.Version()))
230
+	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("git-commit", dockerversion.GITCOMMIT))
231
+	if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
232
+		httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("kernel", kernelVersion.String()))
233
+	}
234
+	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("os", runtime.GOOS))
235
+	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("arch", runtime.GOARCH))
236
+
237
+	userAgent := requestdecorator.AppendVersions(req.UserAgent(), httpVersion...)
238
+
239
+	req.Header.Set("User-Agent", userAgent)
240
+
241
+	for k, v := range tr.Headers {
242
+		req.Header[k] = v
243
+	}
244
+	return tr.RoundTripper.RoundTrip(req)
245
+}
246
+
247
+type debugTransport struct{ http.RoundTripper }
248
+
249
+func (tr debugTransport) RoundTrip(req *http.Request) (*http.Response, error) {
250
+	dump, err := httputil.DumpRequestOut(req, false)
251
+	if err != nil {
252
+		fmt.Println("could not dump request")
253
+	}
254
+	fmt.Println(string(dump))
255
+	resp, err := tr.RoundTripper.RoundTrip(req)
256
+	if err != nil {
257
+		return nil, err
258
+	}
259
+	dump, err = httputil.DumpResponse(resp, false)
260
+	if err != nil {
261
+		fmt.Println("could not dump response")
262
+	}
263
+	fmt.Println(string(dump))
264
+	return resp, err
265
+}
266
+
267
+func HTTPClient(transport http.RoundTripper) *http.Client {
268
+	if transport == nil {
269
+		transport = NewTransport(ConnectTimeout, true)
270
+	}
271
+
272
+	return &http.Client{
273
+		Transport:     transport,
274
+		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
275
+	}
158 276
 }
159 277
 
160 278
 func trustedLocation(req *http.Request) bool {
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"testing"
9 9
 
10 10
 	"github.com/docker/docker/cliconfig"
11
-	"github.com/docker/docker/pkg/requestdecorator"
12 11
 )
13 12
 
14 13
 var (
... ...
@@ -26,38 +25,27 @@ func spawnTestRegistrySession(t *testing.T) *Session {
26 26
 	if err != nil {
27 27
 		t.Fatal(err)
28 28
 	}
29
-	r, err := NewSession(authConfig, requestdecorator.NewRequestFactory(), endpoint, true)
29
+	var tr http.RoundTripper = debugTransport{NewTransport(ReceiveTimeout, endpoint.IsSecure)}
30
+	tr = &DockerHeaders{&authTransport{RoundTripper: tr, AuthConfig: authConfig}, nil}
31
+	client := HTTPClient(tr)
32
+	r, err := NewSession(client, authConfig, endpoint)
30 33
 	if err != nil {
31 34
 		t.Fatal(err)
32 35
 	}
36
+	// In a normal scenario for the v1 registry, the client should send a `X-Docker-Token: true`
37
+	// header while authenticating, in order to retrieve a token that can be later used to
38
+	// perform authenticated actions.
39
+	//
40
+	// The mock v1 registry does not support that, (TODO(tiborvass): support it), instead,
41
+	// it will consider authenticated any request with the header `X-Docker-Token: fake-token`.
42
+	//
43
+	// Because we know that the client's transport is an `*authTransport` we simply cast it,
44
+	// in order to set the internal cached token to the fake token, and thus send that fake token
45
+	// upon every subsequent requests.
46
+	r.client.Transport.(*authTransport).token = token
33 47
 	return r
34 48
 }
35 49
 
36
-func TestPublicSession(t *testing.T) {
37
-	authConfig := &cliconfig.AuthConfig{}
38
-
39
-	getSessionDecorators := func(index *IndexInfo) int {
40
-		endpoint, err := NewEndpoint(index)
41
-		if err != nil {
42
-			t.Fatal(err)
43
-		}
44
-		r, err := NewSession(authConfig, requestdecorator.NewRequestFactory(), endpoint, true)
45
-		if err != nil {
46
-			t.Fatal(err)
47
-		}
48
-		return len(r.reqFactory.GetDecorators())
49
-	}
50
-
51
-	decorators := getSessionDecorators(makeIndex("/v1/"))
52
-	assertEqual(t, decorators, 0, "Expected no decorator on http session")
53
-
54
-	decorators = getSessionDecorators(makeHttpsIndex("/v1/"))
55
-	assertNotEqual(t, decorators, 0, "Expected decorator on https session")
56
-
57
-	decorators = getSessionDecorators(makePublicIndex())
58
-	assertEqual(t, decorators, 0, "Expected no decorator on public session")
59
-}
60
-
61 50
 func TestPingRegistryEndpoint(t *testing.T) {
62 51
 	testPing := func(index *IndexInfo, expectedStandalone bool, assertMessage string) {
63 52
 		ep, err := NewEndpoint(index)
... ...
@@ -170,7 +158,7 @@ func TestEndpoint(t *testing.T) {
170 170
 
171 171
 func TestGetRemoteHistory(t *testing.T) {
172 172
 	r := spawnTestRegistrySession(t)
173
-	hist, err := r.GetRemoteHistory(imageID, makeURL("/v1/"), token)
173
+	hist, err := r.GetRemoteHistory(imageID, makeURL("/v1/"))
174 174
 	if err != nil {
175 175
 		t.Fatal(err)
176 176
 	}
... ...
@@ -182,16 +170,16 @@ func TestGetRemoteHistory(t *testing.T) {
182 182
 
183 183
 func TestLookupRemoteImage(t *testing.T) {
184 184
 	r := spawnTestRegistrySession(t)
185
-	err := r.LookupRemoteImage(imageID, makeURL("/v1/"), token)
185
+	err := r.LookupRemoteImage(imageID, makeURL("/v1/"))
186 186
 	assertEqual(t, err, nil, "Expected error of remote lookup to nil")
187
-	if err := r.LookupRemoteImage("abcdef", makeURL("/v1/"), token); err == nil {
187
+	if err := r.LookupRemoteImage("abcdef", makeURL("/v1/")); err == nil {
188 188
 		t.Fatal("Expected error of remote lookup to not nil")
189 189
 	}
190 190
 }
191 191
 
192 192
 func TestGetRemoteImageJSON(t *testing.T) {
193 193
 	r := spawnTestRegistrySession(t)
194
-	json, size, err := r.GetRemoteImageJSON(imageID, makeURL("/v1/"), token)
194
+	json, size, err := r.GetRemoteImageJSON(imageID, makeURL("/v1/"))
195 195
 	if err != nil {
196 196
 		t.Fatal(err)
197 197
 	}
... ...
@@ -200,7 +188,7 @@ func TestGetRemoteImageJSON(t *testing.T) {
200 200
 		t.Fatal("Expected non-empty json")
201 201
 	}
202 202
 
203
-	_, _, err = r.GetRemoteImageJSON("abcdef", makeURL("/v1/"), token)
203
+	_, _, err = r.GetRemoteImageJSON("abcdef", makeURL("/v1/"))
204 204
 	if err == nil {
205 205
 		t.Fatal("Expected image not found error")
206 206
 	}
... ...
@@ -208,7 +196,7 @@ func TestGetRemoteImageJSON(t *testing.T) {
208 208
 
209 209
 func TestGetRemoteImageLayer(t *testing.T) {
210 210
 	r := spawnTestRegistrySession(t)
211
-	data, err := r.GetRemoteImageLayer(imageID, makeURL("/v1/"), token, 0)
211
+	data, err := r.GetRemoteImageLayer(imageID, makeURL("/v1/"), 0)
212 212
 	if err != nil {
213 213
 		t.Fatal(err)
214 214
 	}
... ...
@@ -216,7 +204,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
216 216
 		t.Fatal("Expected non-nil data result")
217 217
 	}
218 218
 
219
-	_, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), token, 0)
219
+	_, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), 0)
220 220
 	if err == nil {
221 221
 		t.Fatal("Expected image not found error")
222 222
 	}
... ...
@@ -224,14 +212,14 @@ func TestGetRemoteImageLayer(t *testing.T) {
224 224
 
225 225
 func TestGetRemoteTags(t *testing.T) {
226 226
 	r := spawnTestRegistrySession(t)
227
-	tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO, token)
227
+	tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO)
228 228
 	if err != nil {
229 229
 		t.Fatal(err)
230 230
 	}
231 231
 	assertEqual(t, len(tags), 1, "Expected one tag")
232 232
 	assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
233 233
 
234
-	_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz", token)
234
+	_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz")
235 235
 	if err == nil {
236 236
 		t.Fatal("Expected error when fetching tags for bogus repo")
237 237
 	}
... ...
@@ -265,7 +253,7 @@ func TestPushImageJSONRegistry(t *testing.T) {
265 265
 		Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
266 266
 	}
267 267
 
268
-	err := r.PushImageJSONRegistry(imgData, []byte{0x42, 0xdf, 0x0}, makeURL("/v1/"), token)
268
+	err := r.PushImageJSONRegistry(imgData, []byte{0x42, 0xdf, 0x0}, makeURL("/v1/"))
269 269
 	if err != nil {
270 270
 		t.Fatal(err)
271 271
 	}
... ...
@@ -274,7 +262,7 @@ func TestPushImageJSONRegistry(t *testing.T) {
274 274
 func TestPushImageLayerRegistry(t *testing.T) {
275 275
 	r := spawnTestRegistrySession(t)
276 276
 	layer := strings.NewReader("")
277
-	_, _, err := r.PushImageLayerRegistry(imageID, layer, makeURL("/v1/"), token, []byte{})
277
+	_, _, err := r.PushImageLayerRegistry(imageID, layer, makeURL("/v1/"), []byte{})
278 278
 	if err != nil {
279 279
 		t.Fatal(err)
280 280
 	}
... ...
@@ -694,7 +682,7 @@ func TestNewIndexInfo(t *testing.T) {
694 694
 
695 695
 func TestPushRegistryTag(t *testing.T) {
696 696
 	r := spawnTestRegistrySession(t)
697
-	err := r.PushRegistryTag("foo42/bar", imageID, "stable", makeURL("/v1/"), token)
697
+	err := r.PushRegistryTag("foo42/bar", imageID, "stable", makeURL("/v1/"))
698 698
 	if err != nil {
699 699
 		t.Fatal(err)
700 700
 	}
... ...
@@ -32,7 +32,7 @@ func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
32 32
 		return "", err
33 33
 	}
34 34
 	authConfig.ServerAddress = endpoint.String()
35
-	return Login(authConfig, endpoint, HTTPRequestFactory(nil))
35
+	return Login(authConfig, endpoint)
36 36
 }
37 37
 
38 38
 // Search queries the public registry for images matching the specified
... ...
@@ -42,12 +42,13 @@ func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers
42 42
 	if err != nil {
43 43
 		return nil, err
44 44
 	}
45
+
45 46
 	// *TODO: Search multiple indexes.
46 47
 	endpoint, err := repoInfo.GetEndpoint()
47 48
 	if err != nil {
48 49
 		return nil, err
49 50
 	}
50
-	r, err := NewSession(authConfig, HTTPRequestFactory(headers), endpoint, true)
51
+	r, err := NewSession(endpoint.HTTPClient(), authConfig, endpoint)
51 52
 	if err != nil {
52 53
 		return nil, err
53 54
 	}
... ...
@@ -3,6 +3,7 @@ package registry
3 3
 import (
4 4
 	"bytes"
5 5
 	"crypto/sha256"
6
+	"errors"
6 7
 	// this is required for some certificates
7 8
 	_ "crypto/sha512"
8 9
 	"encoding/hex"
... ...
@@ -20,64 +21,105 @@ import (
20 20
 	"github.com/Sirupsen/logrus"
21 21
 	"github.com/docker/docker/cliconfig"
22 22
 	"github.com/docker/docker/pkg/httputils"
23
-	"github.com/docker/docker/pkg/requestdecorator"
24 23
 	"github.com/docker/docker/pkg/tarsum"
25 24
 )
26 25
 
27 26
 type Session struct {
28
-	authConfig    *cliconfig.AuthConfig
29
-	reqFactory    *requestdecorator.RequestFactory
30 27
 	indexEndpoint *Endpoint
31
-	jar           *cookiejar.Jar
32
-	timeout       TimeoutType
28
+	client        *http.Client
29
+	// TODO(tiborvass): remove authConfig
30
+	authConfig *cliconfig.AuthConfig
33 31
 }
34 32
 
35
-func NewSession(authConfig *cliconfig.AuthConfig, factory *requestdecorator.RequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
36
-	r = &Session{
37
-		authConfig:    authConfig,
38
-		indexEndpoint: endpoint,
39
-	}
33
+// authTransport handles the auth layer when communicating with a v1 registry (private or official)
34
+//
35
+// For private v1 registries, set alwaysSetBasicAuth to true.
36
+//
37
+// For the official v1 registry, if there isn't already an Authorization header in the request,
38
+// but there is an X-Docker-Token header set to true, then Basic Auth will be used to set the Authorization header.
39
+// After sending the request with the provided base http.RoundTripper, if an X-Docker-Token header, representing
40
+// a token, is present in the response, then it gets cached and sent in the Authorization header of all subsequent
41
+// requests.
42
+//
43
+// If the server sends a token without the client having requested it, it is ignored.
44
+//
45
+// This RoundTripper also has a CancelRequest method important for correct timeout handling.
46
+type authTransport struct {
47
+	http.RoundTripper
48
+	*cliconfig.AuthConfig
49
+
50
+	alwaysSetBasicAuth bool
51
+	token              []string
52
+}
40 53
 
41
-	if timeout {
42
-		r.timeout = ReceiveTimeout
54
+func (tr *authTransport) RoundTrip(req *http.Request) (*http.Response, error) {
55
+	req = cloneRequest(req)
56
+
57
+	if tr.alwaysSetBasicAuth {
58
+		req.SetBasicAuth(tr.Username, tr.Password)
59
+		return tr.RoundTripper.RoundTrip(req)
43 60
 	}
44 61
 
45
-	r.jar, err = cookiejar.New(nil)
62
+	var askedForToken bool
63
+
64
+	// Don't override
65
+	if req.Header.Get("Authorization") == "" {
66
+		if req.Header.Get("X-Docker-Token") == "true" {
67
+			req.SetBasicAuth(tr.Username, tr.Password)
68
+			askedForToken = true
69
+		} else if len(tr.token) > 0 {
70
+			req.Header.Set("Authorization", "Token "+strings.Join(tr.token, ","))
71
+		}
72
+	}
73
+	resp, err := tr.RoundTripper.RoundTrip(req)
46 74
 	if err != nil {
47 75
 		return nil, err
48 76
 	}
77
+	if askedForToken && len(resp.Header["X-Docker-Token"]) > 0 {
78
+		tr.token = resp.Header["X-Docker-Token"]
79
+	}
80
+	return resp, nil
81
+}
82
+
83
+// TODO(tiborvass): remove authConfig param once registry client v2 is vendored
84
+func NewSession(client *http.Client, authConfig *cliconfig.AuthConfig, endpoint *Endpoint) (r *Session, err error) {
85
+	r = &Session{
86
+		authConfig:    authConfig,
87
+		client:        client,
88
+		indexEndpoint: endpoint,
89
+	}
90
+
91
+	var alwaysSetBasicAuth bool
49 92
 
50 93
 	// If we're working with a standalone private registry over HTTPS, send Basic Auth headers
51
-	// alongside our requests.
52
-	if r.indexEndpoint.VersionString(1) != IndexServerAddress() && r.indexEndpoint.URL.Scheme == "https" {
53
-		info, err := r.indexEndpoint.Ping()
94
+	// alongside all our requests.
95
+	if endpoint.VersionString(1) != IndexServerAddress() && endpoint.URL.Scheme == "https" {
96
+		info, err := endpoint.Ping()
54 97
 		if err != nil {
55 98
 			return nil, err
56 99
 		}
57
-		if info.Standalone && authConfig != nil && factory != nil {
58
-			logrus.Debugf("Endpoint %s is eligible for private registry. Enabling decorator.", r.indexEndpoint.String())
59
-			dec := requestdecorator.NewAuthDecorator(authConfig.Username, authConfig.Password)
60
-			factory.AddDecorator(dec)
100
+
101
+		if info.Standalone && authConfig != nil {
102
+			logrus.Debugf("Endpoint %s is eligible for private registry. Enabling decorator.", endpoint.String())
103
+			alwaysSetBasicAuth = true
61 104
 		}
62 105
 	}
63 106
 
64
-	r.reqFactory = factory
65
-	return r, nil
66
-}
107
+	client.Transport = &authTransport{RoundTripper: client.Transport, AuthConfig: authConfig, alwaysSetBasicAuth: alwaysSetBasicAuth}
67 108
 
68
-func (r *Session) doRequest(req *http.Request) (*http.Response, *http.Client, error) {
69
-	return doRequest(req, r.jar, r.timeout, r.indexEndpoint.IsSecure)
109
+	jar, err := cookiejar.New(nil)
110
+	if err != nil {
111
+		return nil, errors.New("cookiejar.New is not supposed to return an error")
112
+	}
113
+	client.Jar = jar
114
+
115
+	return r, nil
70 116
 }
71 117
 
72 118
 // Retrieve the history of a given image from the Registry.
73 119
 // Return a list of the parent's json (requested image included)
74
-func (r *Session) GetRemoteHistory(imgID, registry string, token []string) ([]string, error) {
75
-	req, err := r.reqFactory.NewRequest("GET", registry+"images/"+imgID+"/ancestry", nil)
76
-	if err != nil {
77
-		return nil, err
78
-	}
79
-	setTokenAuth(req, token)
80
-	res, _, err := r.doRequest(req)
120
+func (r *Session) GetRemoteHistory(imgID, registry string) ([]string, error) {
121
+	res, err := r.client.Get(registry + "images/" + imgID + "/ancestry")
81 122
 	if err != nil {
82 123
 		return nil, err
83 124
 	}
... ...
@@ -89,27 +131,18 @@ func (r *Session) GetRemoteHistory(imgID, registry string, token []string) ([]st
89 89
 		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res)
90 90
 	}
91 91
 
92
-	jsonString, err := ioutil.ReadAll(res.Body)
93
-	if err != nil {
94
-		return nil, fmt.Errorf("Error while reading the http response: %s", err)
92
+	var history []string
93
+	if err := json.NewDecoder(res.Body).Decode(&history); err != nil {
94
+		return nil, fmt.Errorf("Error while reading the http response: %v", err)
95 95
 	}
96 96
 
97
-	logrus.Debugf("Ancestry: %s", jsonString)
98
-	history := new([]string)
99
-	if err := json.Unmarshal(jsonString, history); err != nil {
100
-		return nil, err
101
-	}
102
-	return *history, nil
97
+	logrus.Debugf("Ancestry: %v", history)
98
+	return history, nil
103 99
 }
104 100
 
105 101
 // Check if an image exists in the Registry
106
-func (r *Session) LookupRemoteImage(imgID, registry string, token []string) error {
107
-	req, err := r.reqFactory.NewRequest("GET", registry+"images/"+imgID+"/json", nil)
108
-	if err != nil {
109
-		return err
110
-	}
111
-	setTokenAuth(req, token)
112
-	res, _, err := r.doRequest(req)
102
+func (r *Session) LookupRemoteImage(imgID, registry string) error {
103
+	res, err := r.client.Get(registry + "images/" + imgID + "/json")
113 104
 	if err != nil {
114 105
 		return err
115 106
 	}
... ...
@@ -121,14 +154,8 @@ func (r *Session) LookupRemoteImage(imgID, registry string, token []string) erro
121 121
 }
122 122
 
123 123
 // Retrieve an image from the Registry.
124
-func (r *Session) GetRemoteImageJSON(imgID, registry string, token []string) ([]byte, int, error) {
125
-	// Get the JSON
126
-	req, err := r.reqFactory.NewRequest("GET", registry+"images/"+imgID+"/json", nil)
127
-	if err != nil {
128
-		return nil, -1, fmt.Errorf("Failed to download json: %s", err)
129
-	}
130
-	setTokenAuth(req, token)
131
-	res, _, err := r.doRequest(req)
124
+func (r *Session) GetRemoteImageJSON(imgID, registry string) ([]byte, int, error) {
125
+	res, err := r.client.Get(registry + "images/" + imgID + "/json")
132 126
 	if err != nil {
133 127
 		return nil, -1, fmt.Errorf("Failed to download json: %s", err)
134 128
 	}
... ...
@@ -147,44 +174,44 @@ func (r *Session) GetRemoteImageJSON(imgID, registry string, token []string) ([]
147 147
 
148 148
 	jsonString, err := ioutil.ReadAll(res.Body)
149 149
 	if err != nil {
150
-		return nil, -1, fmt.Errorf("Failed to parse downloaded json: %s (%s)", err, jsonString)
150
+		return nil, -1, fmt.Errorf("Failed to parse downloaded json: %v (%s)", err, jsonString)
151 151
 	}
152 152
 	return jsonString, imageSize, nil
153 153
 }
154 154
 
155
-func (r *Session) GetRemoteImageLayer(imgID, registry string, token []string, imgSize int64) (io.ReadCloser, error) {
155
+func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io.ReadCloser, error) {
156 156
 	var (
157 157
 		retries    = 5
158 158
 		statusCode = 0
159
-		client     *http.Client
160 159
 		res        *http.Response
160
+		err        error
161 161
 		imageURL   = fmt.Sprintf("%simages/%s/layer", registry, imgID)
162 162
 	)
163 163
 
164
-	req, err := r.reqFactory.NewRequest("GET", imageURL, nil)
164
+	req, err := http.NewRequest("GET", imageURL, nil)
165 165
 	if err != nil {
166
-		return nil, fmt.Errorf("Error while getting from the server: %s\n", err)
166
+		return nil, fmt.Errorf("Error while getting from the server: %v", err)
167 167
 	}
168
-	setTokenAuth(req, token)
168
+	// TODO: why are we doing retries at this level?
169
+	// These retries should be generic to both v1 and v2
169 170
 	for i := 1; i <= retries; i++ {
170 171
 		statusCode = 0
171
-		res, client, err = r.doRequest(req)
172
-		if err != nil {
173
-			logrus.Debugf("Error contacting registry: %s", err)
174
-			if res != nil {
175
-				if res.Body != nil {
176
-					res.Body.Close()
177
-				}
178
-				statusCode = res.StatusCode
179
-			}
180
-			if i == retries {
181
-				return nil, fmt.Errorf("Server error: Status %d while fetching image layer (%s)",
182
-					statusCode, imgID)
172
+		res, err = r.client.Do(req)
173
+		if err == nil {
174
+			break
175
+		}
176
+		logrus.Debugf("Error contacting registry %s: %v", registry, err)
177
+		if res != nil {
178
+			if res.Body != nil {
179
+				res.Body.Close()
183 180
 			}
184
-			time.Sleep(time.Duration(i) * 5 * time.Second)
185
-			continue
181
+			statusCode = res.StatusCode
182
+		}
183
+		if i == retries {
184
+			return nil, fmt.Errorf("Server error: Status %d while fetching image layer (%s)",
185
+				statusCode, imgID)
186 186
 		}
187
-		break
187
+		time.Sleep(time.Duration(i) * 5 * time.Second)
188 188
 	}
189 189
 
190 190
 	if res.StatusCode != 200 {
... ...
@@ -195,13 +222,13 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, token []string, im
195 195
 
196 196
 	if res.Header.Get("Accept-Ranges") == "bytes" && imgSize > 0 {
197 197
 		logrus.Debugf("server supports resume")
198
-		return httputils.ResumableRequestReaderWithInitialResponse(client, req, 5, imgSize, res), nil
198
+		return httputils.ResumableRequestReaderWithInitialResponse(r.client, req, 5, imgSize, res), nil
199 199
 	}
200 200
 	logrus.Debugf("server doesn't support resume")
201 201
 	return res.Body, nil
202 202
 }
203 203
 
204
-func (r *Session) GetRemoteTags(registries []string, repository string, token []string) (map[string]string, error) {
204
+func (r *Session) GetRemoteTags(registries []string, repository string) (map[string]string, error) {
205 205
 	if strings.Count(repository, "/") == 0 {
206 206
 		// This will be removed once the Registry supports auto-resolution on
207 207
 		// the "library" namespace
... ...
@@ -209,13 +236,7 @@ func (r *Session) GetRemoteTags(registries []string, repository string, token []
209 209
 	}
210 210
 	for _, host := range registries {
211 211
 		endpoint := fmt.Sprintf("%srepositories/%s/tags", host, repository)
212
-		req, err := r.reqFactory.NewRequest("GET", endpoint, nil)
213
-
214
-		if err != nil {
215
-			return nil, err
216
-		}
217
-		setTokenAuth(req, token)
218
-		res, _, err := r.doRequest(req)
212
+		res, err := r.client.Get(endpoint)
219 213
 		if err != nil {
220 214
 			return nil, err
221 215
 		}
... ...
@@ -263,16 +284,13 @@ func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
263 263
 
264 264
 	logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
265 265
 
266
-	req, err := r.reqFactory.NewRequest("GET", repositoryTarget, nil)
266
+	req, err := http.NewRequest("GET", repositoryTarget, nil)
267 267
 	if err != nil {
268 268
 		return nil, err
269 269
 	}
270
-	if r.authConfig != nil && len(r.authConfig.Username) > 0 {
271
-		req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
272
-	}
270
+	// this will set basic auth in r.client.Transport and send cached X-Docker-Token headers for all subsequent requests
273 271
 	req.Header.Set("X-Docker-Token", "true")
274
-
275
-	res, _, err := r.doRequest(req)
272
+	res, err := r.client.Do(req)
276 273
 	if err != nil {
277 274
 		return nil, err
278 275
 	}
... ...
@@ -292,11 +310,6 @@ func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
292 292
 		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote, errBody), res)
293 293
 	}
294 294
 
295
-	var tokens []string
296
-	if res.Header.Get("X-Docker-Token") != "" {
297
-		tokens = res.Header["X-Docker-Token"]
298
-	}
299
-
300 295
 	var endpoints []string
301 296
 	if res.Header.Get("X-Docker-Endpoints") != "" {
302 297
 		endpoints, err = buildEndpointsList(res.Header["X-Docker-Endpoints"], r.indexEndpoint.VersionString(1))
... ...
@@ -322,29 +335,29 @@ func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
322 322
 	return &RepositoryData{
323 323
 		ImgList:   imgsData,
324 324
 		Endpoints: endpoints,
325
-		Tokens:    tokens,
326 325
 	}, nil
327 326
 }
328 327
 
329
-func (r *Session) PushImageChecksumRegistry(imgData *ImgData, registry string, token []string) error {
328
+func (r *Session) PushImageChecksumRegistry(imgData *ImgData, registry string) error {
329
+
330
+	u := registry + "images/" + imgData.ID + "/checksum"
330 331
 
331
-	logrus.Debugf("[registry] Calling PUT %s", registry+"images/"+imgData.ID+"/checksum")
332
+	logrus.Debugf("[registry] Calling PUT %s", u)
332 333
 
333
-	req, err := r.reqFactory.NewRequest("PUT", registry+"images/"+imgData.ID+"/checksum", nil)
334
+	req, err := http.NewRequest("PUT", u, nil)
334 335
 	if err != nil {
335 336
 		return err
336 337
 	}
337
-	setTokenAuth(req, token)
338 338
 	req.Header.Set("X-Docker-Checksum", imgData.Checksum)
339 339
 	req.Header.Set("X-Docker-Checksum-Payload", imgData.ChecksumPayload)
340 340
 
341
-	res, _, err := r.doRequest(req)
341
+	res, err := r.client.Do(req)
342 342
 	if err != nil {
343
-		return fmt.Errorf("Failed to upload metadata: %s", err)
343
+		return fmt.Errorf("Failed to upload metadata: %v", err)
344 344
 	}
345 345
 	defer res.Body.Close()
346 346
 	if len(res.Cookies()) > 0 {
347
-		r.jar.SetCookies(req.URL, res.Cookies())
347
+		r.client.Jar.SetCookies(req.URL, res.Cookies())
348 348
 	}
349 349
 	if res.StatusCode != 200 {
350 350
 		errBody, err := ioutil.ReadAll(res.Body)
... ...
@@ -363,18 +376,19 @@ func (r *Session) PushImageChecksumRegistry(imgData *ImgData, registry string, t
363 363
 }
364 364
 
365 365
 // Push a local image to the registry
366
-func (r *Session) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error {
366
+func (r *Session) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, registry string) error {
367 367
 
368
-	logrus.Debugf("[registry] Calling PUT %s", registry+"images/"+imgData.ID+"/json")
368
+	u := registry + "images/" + imgData.ID + "/json"
369 369
 
370
-	req, err := r.reqFactory.NewRequest("PUT", registry+"images/"+imgData.ID+"/json", bytes.NewReader(jsonRaw))
370
+	logrus.Debugf("[registry] Calling PUT %s", u)
371
+
372
+	req, err := http.NewRequest("PUT", u, bytes.NewReader(jsonRaw))
371 373
 	if err != nil {
372 374
 		return err
373 375
 	}
374 376
 	req.Header.Add("Content-type", "application/json")
375
-	setTokenAuth(req, token)
376 377
 
377
-	res, _, err := r.doRequest(req)
378
+	res, err := r.client.Do(req)
378 379
 	if err != nil {
379 380
 		return fmt.Errorf("Failed to upload metadata: %s", err)
380 381
 	}
... ...
@@ -398,9 +412,11 @@ func (r *Session) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regist
398 398
 	return nil
399 399
 }
400 400
 
401
-func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry string, token []string, jsonRaw []byte) (checksum string, checksumPayload string, err error) {
401
+func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry string, jsonRaw []byte) (checksum string, checksumPayload string, err error) {
402
+
403
+	u := registry + "images/" + imgID + "/layer"
402 404
 
403
-	logrus.Debugf("[registry] Calling PUT %s", registry+"images/"+imgID+"/layer")
405
+	logrus.Debugf("[registry] Calling PUT %s", u)
404 406
 
405 407
 	tarsumLayer, err := tarsum.NewTarSum(layer, false, tarsum.Version0)
406 408
 	if err != nil {
... ...
@@ -411,17 +427,16 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
411 411
 	h.Write([]byte{'\n'})
412 412
 	checksumLayer := io.TeeReader(tarsumLayer, h)
413 413
 
414
-	req, err := r.reqFactory.NewRequest("PUT", registry+"images/"+imgID+"/layer", checksumLayer)
414
+	req, err := http.NewRequest("PUT", u, checksumLayer)
415 415
 	if err != nil {
416 416
 		return "", "", err
417 417
 	}
418 418
 	req.Header.Add("Content-Type", "application/octet-stream")
419 419
 	req.ContentLength = -1
420 420
 	req.TransferEncoding = []string{"chunked"}
421
-	setTokenAuth(req, token)
422
-	res, _, err := r.doRequest(req)
421
+	res, err := r.client.Do(req)
423 422
 	if err != nil {
424
-		return "", "", fmt.Errorf("Failed to upload layer: %s", err)
423
+		return "", "", fmt.Errorf("Failed to upload layer: %v", err)
425 424
 	}
426 425
 	if rc, ok := layer.(io.Closer); ok {
427 426
 		if err := rc.Close(); err != nil {
... ...
@@ -444,19 +459,18 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
444 444
 
445 445
 // push a tag on the registry.
446 446
 // Remote has the format '<user>/<repo>
447
-func (r *Session) PushRegistryTag(remote, revision, tag, registry string, token []string) error {
447
+func (r *Session) PushRegistryTag(remote, revision, tag, registry string) error {
448 448
 	// "jsonify" the string
449 449
 	revision = "\"" + revision + "\""
450 450
 	path := fmt.Sprintf("repositories/%s/tags/%s", remote, tag)
451 451
 
452
-	req, err := r.reqFactory.NewRequest("PUT", registry+path, strings.NewReader(revision))
452
+	req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
453 453
 	if err != nil {
454 454
 		return err
455 455
 	}
456 456
 	req.Header.Add("Content-type", "application/json")
457
-	setTokenAuth(req, token)
458 457
 	req.ContentLength = int64(len(revision))
459
-	res, _, err := r.doRequest(req)
458
+	res, err := r.client.Do(req)
460 459
 	if err != nil {
461 460
 		return err
462 461
 	}
... ...
@@ -491,7 +505,8 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
491 491
 	logrus.Debugf("[registry] PUT %s", u)
492 492
 	logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
493 493
 	headers := map[string][]string{
494
-		"Content-type":   {"application/json"},
494
+		"Content-type": {"application/json"},
495
+		// this will set basic auth in r.client.Transport and send cached X-Docker-Token headers for all subsequent requests
495 496
 		"X-Docker-Token": {"true"},
496 497
 	}
497 498
 	if validate {
... ...
@@ -526,9 +541,6 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
526 526
 			}
527 527
 			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote, errBody), res)
528 528
 		}
529
-		if res.Header.Get("X-Docker-Token") == "" {
530
-			return nil, fmt.Errorf("Index response didn't contain an access token")
531
-		}
532 529
 		tokens = res.Header["X-Docker-Token"]
533 530
 		logrus.Debugf("Auth token: %v", tokens)
534 531
 
... ...
@@ -539,8 +551,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
539 539
 		if err != nil {
540 540
 			return nil, err
541 541
 		}
542
-	}
543
-	if validate {
542
+	} else {
544 543
 		if res.StatusCode != 204 {
545 544
 			errBody, err := ioutil.ReadAll(res.Body)
546 545
 			if err != nil {
... ...
@@ -551,22 +562,20 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
551 551
 	}
552 552
 
553 553
 	return &RepositoryData{
554
-		Tokens:    tokens,
555 554
 		Endpoints: endpoints,
556 555
 	}, nil
557 556
 }
558 557
 
559 558
 func (r *Session) putImageRequest(u string, headers map[string][]string, body []byte) (*http.Response, error) {
560
-	req, err := r.reqFactory.NewRequest("PUT", u, bytes.NewReader(body))
559
+	req, err := http.NewRequest("PUT", u, bytes.NewReader(body))
561 560
 	if err != nil {
562 561
 		return nil, err
563 562
 	}
564
-	req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
565 563
 	req.ContentLength = int64(len(body))
566 564
 	for k, v := range headers {
567 565
 		req.Header[k] = v
568 566
 	}
569
-	response, _, err := r.doRequest(req)
567
+	response, err := r.client.Do(req)
570 568
 	if err != nil {
571 569
 		return nil, err
572 570
 	}
... ...
@@ -580,15 +589,7 @@ func shouldRedirect(response *http.Response) bool {
580 580
 func (r *Session) SearchRepositories(term string) (*SearchResults, error) {
581 581
 	logrus.Debugf("Index server: %s", r.indexEndpoint)
582 582
 	u := r.indexEndpoint.VersionString(1) + "search?q=" + url.QueryEscape(term)
583
-	req, err := r.reqFactory.NewRequest("GET", u, nil)
584
-	if err != nil {
585
-		return nil, err
586
-	}
587
-	if r.authConfig != nil && len(r.authConfig.Username) > 0 {
588
-		req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
589
-	}
590
-	req.Header.Set("X-Docker-Token", "true")
591
-	res, _, err := r.doRequest(req)
583
+	res, err := r.client.Get(u)
592 584
 	if err != nil {
593 585
 		return nil, err
594 586
 	}
... ...
@@ -600,6 +601,7 @@ func (r *Session) SearchRepositories(term string) (*SearchResults, error) {
600 600
 	return result, json.NewDecoder(res.Body).Decode(result)
601 601
 }
602 602
 
603
+// TODO(tiborvass): remove this once registry client v2 is vendored
603 604
 func (r *Session) GetAuthConfig(withPasswd bool) *cliconfig.AuthConfig {
604 605
 	password := ""
605 606
 	if withPasswd {
... ...
@@ -611,9 +613,3 @@ func (r *Session) GetAuthConfig(withPasswd bool) *cliconfig.AuthConfig {
611 611
 		Email:    r.authConfig.Email,
612 612
 	}
613 613
 }
614
-
615
-func setTokenAuth(req *http.Request, token []string) {
616
-	if req.Header.Get("Authorization") == "" { // Don't override
617
-		req.Header.Set("Authorization", "Token "+strings.Join(token, ","))
618
-	}
619
-}
... ...
@@ -77,14 +77,14 @@ func (r *Session) GetV2ImageManifest(ep *Endpoint, imageName, tagName string, au
77 77
 	method := "GET"
78 78
 	logrus.Debugf("[registry] Calling %q %s", method, routeURL)
79 79
 
80
-	req, err := r.reqFactory.NewRequest(method, routeURL, nil)
80
+	req, err := http.NewRequest(method, routeURL, nil)
81 81
 	if err != nil {
82 82
 		return nil, "", err
83 83
 	}
84 84
 	if err := auth.Authorize(req); err != nil {
85 85
 		return nil, "", err
86 86
 	}
87
-	res, _, err := r.doRequest(req)
87
+	res, err := r.client.Do(req)
88 88
 	if err != nil {
89 89
 		return nil, "", err
90 90
 	}
... ...
@@ -118,14 +118,14 @@ func (r *Session) HeadV2ImageBlob(ep *Endpoint, imageName string, dgst digest.Di
118 118
 	method := "HEAD"
119 119
 	logrus.Debugf("[registry] Calling %q %s", method, routeURL)
120 120
 
121
-	req, err := r.reqFactory.NewRequest(method, routeURL, nil)
121
+	req, err := http.NewRequest(method, routeURL, nil)
122 122
 	if err != nil {
123 123
 		return false, err
124 124
 	}
125 125
 	if err := auth.Authorize(req); err != nil {
126 126
 		return false, err
127 127
 	}
128
-	res, _, err := r.doRequest(req)
128
+	res, err := r.client.Do(req)
129 129
 	if err != nil {
130 130
 		return false, err
131 131
 	}
... ...
@@ -152,14 +152,14 @@ func (r *Session) GetV2ImageBlob(ep *Endpoint, imageName string, dgst digest.Dig
152 152
 
153 153
 	method := "GET"
154 154
 	logrus.Debugf("[registry] Calling %q %s", method, routeURL)
155
-	req, err := r.reqFactory.NewRequest(method, routeURL, nil)
155
+	req, err := http.NewRequest(method, routeURL, nil)
156 156
 	if err != nil {
157 157
 		return err
158 158
 	}
159 159
 	if err := auth.Authorize(req); err != nil {
160 160
 		return err
161 161
 	}
162
-	res, _, err := r.doRequest(req)
162
+	res, err := r.client.Do(req)
163 163
 	if err != nil {
164 164
 		return err
165 165
 	}
... ...
@@ -183,14 +183,14 @@ func (r *Session) GetV2ImageBlobReader(ep *Endpoint, imageName string, dgst dige
183 183
 
184 184
 	method := "GET"
185 185
 	logrus.Debugf("[registry] Calling %q %s", method, routeURL)
186
-	req, err := r.reqFactory.NewRequest(method, routeURL, nil)
186
+	req, err := http.NewRequest(method, routeURL, nil)
187 187
 	if err != nil {
188 188
 		return nil, 0, err
189 189
 	}
190 190
 	if err := auth.Authorize(req); err != nil {
191 191
 		return nil, 0, err
192 192
 	}
193
-	res, _, err := r.doRequest(req)
193
+	res, err := r.client.Do(req)
194 194
 	if err != nil {
195 195
 		return nil, 0, err
196 196
 	}
... ...
@@ -220,7 +220,7 @@ func (r *Session) PutV2ImageBlob(ep *Endpoint, imageName string, dgst digest.Dig
220 220
 
221 221
 	method := "PUT"
222 222
 	logrus.Debugf("[registry] Calling %q %s", method, location)
223
-	req, err := r.reqFactory.NewRequest(method, location, ioutil.NopCloser(blobRdr))
223
+	req, err := http.NewRequest(method, location, ioutil.NopCloser(blobRdr))
224 224
 	if err != nil {
225 225
 		return err
226 226
 	}
... ...
@@ -230,7 +230,7 @@ func (r *Session) PutV2ImageBlob(ep *Endpoint, imageName string, dgst digest.Dig
230 230
 	if err := auth.Authorize(req); err != nil {
231 231
 		return err
232 232
 	}
233
-	res, _, err := r.doRequest(req)
233
+	res, err := r.client.Do(req)
234 234
 	if err != nil {
235 235
 		return err
236 236
 	}
... ...
@@ -259,7 +259,7 @@ func (r *Session) initiateBlobUpload(ep *Endpoint, imageName string, auth *Reque
259 259
 	}
260 260
 
261 261
 	logrus.Debugf("[registry] Calling %q %s", "POST", routeURL)
262
-	req, err := r.reqFactory.NewRequest("POST", routeURL, nil)
262
+	req, err := http.NewRequest("POST", routeURL, nil)
263 263
 	if err != nil {
264 264
 		return "", err
265 265
 	}
... ...
@@ -267,7 +267,7 @@ func (r *Session) initiateBlobUpload(ep *Endpoint, imageName string, auth *Reque
267 267
 	if err := auth.Authorize(req); err != nil {
268 268
 		return "", err
269 269
 	}
270
-	res, _, err := r.doRequest(req)
270
+	res, err := r.client.Do(req)
271 271
 	if err != nil {
272 272
 		return "", err
273 273
 	}
... ...
@@ -305,14 +305,14 @@ func (r *Session) PutV2ImageManifest(ep *Endpoint, imageName, tagName string, si
305 305
 
306 306
 	method := "PUT"
307 307
 	logrus.Debugf("[registry] Calling %q %s", method, routeURL)
308
-	req, err := r.reqFactory.NewRequest(method, routeURL, bytes.NewReader(signedManifest))
308
+	req, err := http.NewRequest(method, routeURL, bytes.NewReader(signedManifest))
309 309
 	if err != nil {
310 310
 		return "", err
311 311
 	}
312 312
 	if err := auth.Authorize(req); err != nil {
313 313
 		return "", err
314 314
 	}
315
-	res, _, err := r.doRequest(req)
315
+	res, err := r.client.Do(req)
316 316
 	if err != nil {
317 317
 		return "", err
318 318
 	}
... ...
@@ -366,14 +366,14 @@ func (r *Session) GetV2RemoteTags(ep *Endpoint, imageName string, auth *RequestA
366 366
 	method := "GET"
367 367
 	logrus.Debugf("[registry] Calling %q %s", method, routeURL)
368 368
 
369
-	req, err := r.reqFactory.NewRequest(method, routeURL, nil)
369
+	req, err := http.NewRequest(method, routeURL, nil)
370 370
 	if err != nil {
371 371
 		return nil, err
372 372
 	}
373 373
 	if err := auth.Authorize(req); err != nil {
374 374
 		return nil, err
375 375
 	}
376
-	res, _, err := r.doRequest(req)
376
+	res, err := r.client.Do(req)
377 377
 	if err != nil {
378 378
 		return nil, err
379 379
 	}
... ...
@@ -7,15 +7,13 @@ import (
7 7
 	"net/http"
8 8
 	"net/url"
9 9
 	"strings"
10
-
11
-	"github.com/docker/docker/pkg/requestdecorator"
12 10
 )
13 11
 
14 12
 type tokenResponse struct {
15 13
 	Token string `json:"token"`
16 14
 }
17 15
 
18
-func getToken(username, password string, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) (token string, err error) {
16
+func getToken(username, password string, params map[string]string, registryEndpoint *Endpoint, client *http.Client) (token string, err error) {
19 17
 	realm, ok := params["realm"]
20 18
 	if !ok {
21 19
 		return "", errors.New("no realm specified for token auth challenge")
... ...
@@ -34,7 +32,7 @@ func getToken(username, password string, params map[string]string, registryEndpo
34 34
 		}
35 35
 	}
36 36
 
37
-	req, err := factory.NewRequest("GET", realmURL.String(), nil)
37
+	req, err := http.NewRequest("GET", realmURL.String(), nil)
38 38
 	if err != nil {
39 39
 		return "", err
40 40
 	}