oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/registry/remote/auth/client_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package auth
    17  
    18  import (
    19  	"context"
    20  	"encoding/base64"
    21  	"errors"
    22  	"fmt"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"net/url"
    26  	"reflect"
    27  	"strings"
    28  	"sync/atomic"
    29  	"testing"
    30  
    31  	"oras.land/oras-go/v2/registry/remote/errcode"
    32  )
    33  
    34  func TestClient_SetUserAgent(t *testing.T) {
    35  	wantUserAgent := "test agent"
    36  	var requestCount, wantRequestCount int64
    37  	var successCount, wantSuccessCount int64
    38  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    39  		atomic.AddInt64(&requestCount, 1)
    40  		if r.Method != http.MethodGet || r.URL.Path != "/" {
    41  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    42  			w.WriteHeader(http.StatusNotFound)
    43  			return
    44  		}
    45  		if userAgent := r.UserAgent(); userAgent != wantUserAgent {
    46  			t.Errorf("unexpected User-Agent: %v, want %v", userAgent, wantUserAgent)
    47  			return
    48  		}
    49  		atomic.AddInt64(&successCount, 1)
    50  	}))
    51  	defer ts.Close()
    52  
    53  	var client Client
    54  	client.SetUserAgent(wantUserAgent)
    55  
    56  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
    57  	if err != nil {
    58  		t.Fatalf("failed to create test request: %v", err)
    59  	}
    60  	resp, err := client.Do(req)
    61  	if err != nil {
    62  		t.Fatalf("Client.Do() error = %v", err)
    63  	}
    64  	if resp.StatusCode != http.StatusOK {
    65  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
    66  	}
    67  	if wantRequestCount++; requestCount != wantRequestCount {
    68  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
    69  	}
    70  	if wantSuccessCount++; successCount != wantSuccessCount {
    71  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
    72  	}
    73  }
    74  
    75  func TestClient_Do_Basic_Auth(t *testing.T) {
    76  	username := "test_user"
    77  	password := "test_password"
    78  	var requestCount, wantRequestCount int64
    79  	var successCount, wantSuccessCount int64
    80  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    81  		atomic.AddInt64(&requestCount, 1)
    82  		if r.Method != http.MethodGet || r.URL.Path != "/" {
    83  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    84  			w.WriteHeader(http.StatusNotFound)
    85  			return
    86  		}
    87  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
    88  		if auth := r.Header.Get("Authorization"); auth != header {
    89  			w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
    90  			w.WriteHeader(http.StatusUnauthorized)
    91  			return
    92  		}
    93  		atomic.AddInt64(&successCount, 1)
    94  	}))
    95  	defer ts.Close()
    96  	uri, err := url.Parse(ts.URL)
    97  	if err != nil {
    98  		t.Fatalf("invalid test http server: %v", err)
    99  	}
   100  
   101  	client := &Client{
   102  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   103  			if reg != uri.Host {
   104  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   105  				t.Error(err)
   106  				return EmptyCredential, err
   107  			}
   108  			return Credential{
   109  				Username: username,
   110  				Password: password,
   111  			}, nil
   112  		},
   113  	}
   114  
   115  	// first request
   116  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   117  	if err != nil {
   118  		t.Fatalf("failed to create test request: %v", err)
   119  	}
   120  	resp, err := client.Do(req)
   121  	if err != nil {
   122  		t.Fatalf("Client.Do() error = %v", err)
   123  	}
   124  	if resp.StatusCode != http.StatusOK {
   125  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   126  	}
   127  	if wantRequestCount += 2; requestCount != wantRequestCount {
   128  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   129  	}
   130  	if wantSuccessCount++; successCount != wantSuccessCount {
   131  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   132  	}
   133  
   134  	// credential change
   135  	username = "test_user2"
   136  	password = "test_password2"
   137  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   138  	if err != nil {
   139  		t.Fatalf("failed to create test request: %v", err)
   140  	}
   141  	resp, err = client.Do(req)
   142  	if err != nil {
   143  		t.Fatalf("Client.Do() error = %v", err)
   144  	}
   145  	if resp.StatusCode != http.StatusOK {
   146  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   147  	}
   148  	if wantRequestCount += 2; requestCount != wantRequestCount {
   149  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   150  	}
   151  	if wantSuccessCount++; successCount != wantSuccessCount {
   152  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   153  	}
   154  }
   155  
   156  func TestClient_Do_Basic_Auth_Cached(t *testing.T) {
   157  	username := "test_user"
   158  	password := "test_password"
   159  	var requestCount, wantRequestCount int64
   160  	var successCount, wantSuccessCount int64
   161  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   162  		atomic.AddInt64(&requestCount, 1)
   163  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   164  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   165  			w.WriteHeader(http.StatusNotFound)
   166  			return
   167  		}
   168  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
   169  		if auth := r.Header.Get("Authorization"); auth != header {
   170  			w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
   171  			w.WriteHeader(http.StatusUnauthorized)
   172  			return
   173  		}
   174  		atomic.AddInt64(&successCount, 1)
   175  	}))
   176  	defer ts.Close()
   177  	uri, err := url.Parse(ts.URL)
   178  	if err != nil {
   179  		t.Fatalf("invalid test http server: %v", err)
   180  	}
   181  
   182  	client := &Client{
   183  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   184  			if reg != uri.Host {
   185  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   186  				t.Error(err)
   187  				return EmptyCredential, err
   188  			}
   189  			return Credential{
   190  				Username: username,
   191  				Password: password,
   192  			}, nil
   193  		},
   194  		Cache: NewCache(),
   195  	}
   196  
   197  	// first request
   198  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   199  	if err != nil {
   200  		t.Fatalf("failed to create test request: %v", err)
   201  	}
   202  	resp, err := client.Do(req)
   203  	if err != nil {
   204  		t.Fatalf("Client.Do() error = %v", err)
   205  	}
   206  	if resp.StatusCode != http.StatusOK {
   207  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   208  	}
   209  	if wantRequestCount += 2; requestCount != wantRequestCount {
   210  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   211  	}
   212  	if wantSuccessCount++; successCount != wantSuccessCount {
   213  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   214  	}
   215  
   216  	// repeated request
   217  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   218  	if err != nil {
   219  		t.Fatalf("failed to create test request: %v", err)
   220  	}
   221  	resp, err = client.Do(req)
   222  	if err != nil {
   223  		t.Fatalf("Client.Do() error = %v", err)
   224  	}
   225  	if resp.StatusCode != http.StatusOK {
   226  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   227  	}
   228  	if wantRequestCount++; requestCount != wantRequestCount {
   229  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   230  	}
   231  	if wantSuccessCount++; successCount != wantSuccessCount {
   232  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   233  	}
   234  
   235  	// credential change
   236  	username = "test_user2"
   237  	password = "test_password2"
   238  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   239  	if err != nil {
   240  		t.Fatalf("failed to create test request: %v", err)
   241  	}
   242  	resp, err = client.Do(req)
   243  	if err != nil {
   244  		t.Fatalf("Client.Do() error = %v", err)
   245  	}
   246  	if resp.StatusCode != http.StatusOK {
   247  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   248  	}
   249  	if wantRequestCount += 2; requestCount != wantRequestCount {
   250  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   251  	}
   252  	if wantSuccessCount++; successCount != wantSuccessCount {
   253  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   254  	}
   255  }
   256  
   257  func TestClient_Do_Bearer_AccessToken(t *testing.T) {
   258  	accessToken := "test/access/token"
   259  	var requestCount, wantRequestCount int64
   260  	var successCount, wantSuccessCount int64
   261  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   262  		t.Error("unexecuted attempt of authorization service")
   263  		w.WriteHeader(http.StatusUnauthorized)
   264  	}))
   265  	defer as.Close()
   266  	var service string
   267  	scope := "repository:test:pull,push"
   268  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   269  		atomic.AddInt64(&requestCount, 1)
   270  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   271  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   272  			w.WriteHeader(http.StatusNotFound)
   273  			return
   274  		}
   275  		header := "Bearer " + accessToken
   276  		if auth := r.Header.Get("Authorization"); auth != header {
   277  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
   278  			w.Header().Set("Www-Authenticate", challenge)
   279  			w.WriteHeader(http.StatusUnauthorized)
   280  			return
   281  		}
   282  		atomic.AddInt64(&successCount, 1)
   283  	}))
   284  	defer ts.Close()
   285  	uri, err := url.Parse(ts.URL)
   286  	if err != nil {
   287  		t.Fatalf("invalid test http server: %v", err)
   288  	}
   289  	service = uri.Host
   290  
   291  	client := &Client{
   292  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   293  			if reg != uri.Host {
   294  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   295  				t.Error(err)
   296  				return EmptyCredential, err
   297  			}
   298  			return Credential{
   299  				AccessToken: accessToken,
   300  			}, nil
   301  		},
   302  	}
   303  
   304  	// first request
   305  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   306  	if err != nil {
   307  		t.Fatalf("failed to create test request: %v", err)
   308  	}
   309  	resp, err := client.Do(req)
   310  	if err != nil {
   311  		t.Fatalf("Client.Do() error = %v", err)
   312  	}
   313  	if resp.StatusCode != http.StatusOK {
   314  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   315  	}
   316  	if wantRequestCount += 2; requestCount != wantRequestCount {
   317  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   318  	}
   319  	if wantSuccessCount++; successCount != wantSuccessCount {
   320  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   321  	}
   322  
   323  	// credential change
   324  	accessToken = "test/access/token/2"
   325  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   326  	if err != nil {
   327  		t.Fatalf("failed to create test request: %v", err)
   328  	}
   329  	resp, err = client.Do(req)
   330  	if err != nil {
   331  		t.Fatalf("Client.Do() error = %v", err)
   332  	}
   333  	if resp.StatusCode != http.StatusOK {
   334  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   335  	}
   336  	if wantRequestCount += 2; requestCount != wantRequestCount {
   337  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   338  	}
   339  	if wantSuccessCount++; successCount != wantSuccessCount {
   340  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   341  	}
   342  }
   343  
   344  func TestClient_Do_Bearer_AccessToken_Cached(t *testing.T) {
   345  	accessToken := "test/access/token"
   346  	var requestCount, wantRequestCount int64
   347  	var successCount, wantSuccessCount int64
   348  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   349  		t.Error("unexecuted attempt of authorization service")
   350  		w.WriteHeader(http.StatusUnauthorized)
   351  	}))
   352  	defer as.Close()
   353  	var service string
   354  	scope := "repository:test:pull,push"
   355  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   356  		atomic.AddInt64(&requestCount, 1)
   357  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   358  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   359  			w.WriteHeader(http.StatusNotFound)
   360  			return
   361  		}
   362  		header := "Bearer " + accessToken
   363  		if auth := r.Header.Get("Authorization"); auth != header {
   364  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
   365  			w.Header().Set("Www-Authenticate", challenge)
   366  			w.WriteHeader(http.StatusUnauthorized)
   367  			return
   368  		}
   369  		atomic.AddInt64(&successCount, 1)
   370  	}))
   371  	defer ts.Close()
   372  	uri, err := url.Parse(ts.URL)
   373  	if err != nil {
   374  		t.Fatalf("invalid test http server: %v", err)
   375  	}
   376  	service = uri.Host
   377  
   378  	client := &Client{
   379  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   380  			if reg != uri.Host {
   381  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   382  				t.Error(err)
   383  				return EmptyCredential, err
   384  			}
   385  			return Credential{
   386  				AccessToken: accessToken,
   387  			}, nil
   388  		},
   389  		Cache: NewCache(),
   390  	}
   391  
   392  	// first request
   393  	ctx := WithScopes(context.Background(), scope)
   394  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   395  	if err != nil {
   396  		t.Fatalf("failed to create test request: %v", err)
   397  	}
   398  	resp, err := client.Do(req)
   399  	if err != nil {
   400  		t.Fatalf("Client.Do() error = %v", err)
   401  	}
   402  	if resp.StatusCode != http.StatusOK {
   403  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   404  	}
   405  	if wantRequestCount += 2; requestCount != wantRequestCount {
   406  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   407  	}
   408  	if wantSuccessCount++; successCount != wantSuccessCount {
   409  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   410  	}
   411  
   412  	// repeated request
   413  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   414  	if err != nil {
   415  		t.Fatalf("failed to create test request: %v", err)
   416  	}
   417  	resp, err = client.Do(req)
   418  	if err != nil {
   419  		t.Fatalf("Client.Do() error = %v", err)
   420  	}
   421  	if resp.StatusCode != http.StatusOK {
   422  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   423  	}
   424  	if wantRequestCount++; requestCount != wantRequestCount {
   425  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   426  	}
   427  	if wantSuccessCount++; successCount != wantSuccessCount {
   428  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   429  	}
   430  
   431  	// credential change
   432  	accessToken = "test/access/token/2"
   433  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   434  	if err != nil {
   435  		t.Fatalf("failed to create test request: %v", err)
   436  	}
   437  	resp, err = client.Do(req)
   438  	if err != nil {
   439  		t.Fatalf("Client.Do() error = %v", err)
   440  	}
   441  	if resp.StatusCode != http.StatusOK {
   442  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   443  	}
   444  	if wantRequestCount += 2; requestCount != wantRequestCount {
   445  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   446  	}
   447  	if wantSuccessCount++; successCount != wantSuccessCount {
   448  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   449  	}
   450  }
   451  
   452  func TestClient_Do_Bearer_AccessToken_Cached_PerHost(t *testing.T) {
   453  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   454  		t.Error("unexecuted attempt of authorization service")
   455  		w.WriteHeader(http.StatusUnauthorized)
   456  	}))
   457  	defer as.Close()
   458  	// set up server 1
   459  	var requestCount1, wantRequestCount1 int64
   460  	var successCount1, wantSuccessCount1 int64
   461  	var service1 string
   462  	scope1 := "repository:test:pull"
   463  	accessToken1 := "test/access/token/1"
   464  	ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   465  		atomic.AddInt64(&requestCount1, 1)
   466  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   467  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   468  			w.WriteHeader(http.StatusNotFound)
   469  			return
   470  		}
   471  		header := "Bearer " + accessToken1
   472  		if auth := r.Header.Get("Authorization"); auth != header {
   473  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service1, scope1)
   474  			w.Header().Set("Www-Authenticate", challenge)
   475  			w.WriteHeader(http.StatusUnauthorized)
   476  			return
   477  		}
   478  		atomic.AddInt64(&successCount1, 1)
   479  	}))
   480  	defer ts1.Close()
   481  	uri1, err := url.Parse(ts1.URL)
   482  	if err != nil {
   483  		t.Fatalf("invalid test http server: %v", err)
   484  	}
   485  	service1 = uri1.Host
   486  	client1 := &Client{
   487  		Credential: StaticCredential(uri1.Host, Credential{
   488  			AccessToken: accessToken1,
   489  		}),
   490  		Cache: NewCache(),
   491  	}
   492  
   493  	// set up server 2
   494  	var requestCount2, wantRequestCount2 int64
   495  	var successCount2, wantSuccessCount2 int64
   496  	var service2 string
   497  	scope2 := "repository:test:pull,push"
   498  	accessToken2 := "test/access/token/2"
   499  	ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   500  		atomic.AddInt64(&requestCount2, 1)
   501  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   502  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   503  			w.WriteHeader(http.StatusNotFound)
   504  			return
   505  		}
   506  		header := "Bearer " + accessToken2
   507  		if auth := r.Header.Get("Authorization"); auth != header {
   508  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service2, scope2)
   509  			w.Header().Set("Www-Authenticate", challenge)
   510  			w.WriteHeader(http.StatusUnauthorized)
   511  			return
   512  		}
   513  		atomic.AddInt64(&successCount2, 1)
   514  	}))
   515  	defer ts2.Close()
   516  	uri2, err := url.Parse(ts2.URL)
   517  	if err != nil {
   518  		t.Fatalf("invalid test http server: %v", err)
   519  	}
   520  	service2 = uri2.Host
   521  	client2 := &Client{
   522  		Credential: StaticCredential(uri2.Host, Credential{
   523  			AccessToken: accessToken2,
   524  		}),
   525  		Cache: NewCache(),
   526  	}
   527  
   528  	ctx := context.Background()
   529  	ctx = WithScopesForHost(ctx, uri1.Host, scope1)
   530  	ctx = WithScopesForHost(ctx, uri2.Host, scope2)
   531  	// first request to server 1
   532  	req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
   533  	if err != nil {
   534  		t.Fatalf("failed to create test request: %v", err)
   535  	}
   536  	resp1, err := client1.Do(req1)
   537  	if err != nil {
   538  		t.Fatalf("Client.Do() error = %v", err)
   539  	}
   540  	if resp1.StatusCode != http.StatusOK {
   541  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
   542  	}
   543  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
   544  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
   545  	}
   546  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
   547  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
   548  	}
   549  	// first request to server 2
   550  	req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
   551  	if err != nil {
   552  		t.Fatalf("failed to create test request: %v", err)
   553  	}
   554  	resp2, err := client2.Do(req2)
   555  	if err != nil {
   556  		t.Fatalf("Client.Do() error = %v", err)
   557  	}
   558  	if resp2.StatusCode != http.StatusOK {
   559  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
   560  	}
   561  	if wantRequestCount2 += 2; requestCount1 != wantRequestCount2 {
   562  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
   563  	}
   564  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
   565  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
   566  	}
   567  
   568  	// repeated request to server 1
   569  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
   570  	if err != nil {
   571  		t.Fatalf("failed to create test request: %v", err)
   572  	}
   573  	resp1, err = client1.Do(req1)
   574  	if err != nil {
   575  		t.Fatalf("Client.Do() error = %v", err)
   576  	}
   577  	if resp1.StatusCode != http.StatusOK {
   578  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
   579  	}
   580  	if wantRequestCount1++; requestCount1 != wantRequestCount1 {
   581  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
   582  	}
   583  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
   584  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
   585  	}
   586  	// repeated request to server 2
   587  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
   588  	if err != nil {
   589  		t.Fatalf("failed to create test request: %v", err)
   590  	}
   591  	resp2, err = client2.Do(req2)
   592  	if err != nil {
   593  		t.Fatalf("Client.Do() error = %v", err)
   594  	}
   595  	if resp2.StatusCode != http.StatusOK {
   596  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
   597  	}
   598  	if wantRequestCount2++; requestCount2 != wantRequestCount2 {
   599  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
   600  	}
   601  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
   602  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
   603  	}
   604  
   605  	// credential change for server 1
   606  	accessToken1 = "test/access/token/1/new"
   607  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
   608  	if err != nil {
   609  		t.Fatalf("failed to create test request: %v", err)
   610  	}
   611  	client1.Credential = StaticCredential(uri1.Host, Credential{
   612  		AccessToken: accessToken1,
   613  	})
   614  	resp1, err = client1.Do(req1)
   615  	if err != nil {
   616  		t.Fatalf("Client.Do() error = %v", err)
   617  	}
   618  	if resp1.StatusCode != http.StatusOK {
   619  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
   620  	}
   621  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
   622  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
   623  	}
   624  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
   625  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
   626  	}
   627  	// credential change for server 2
   628  	accessToken2 = "test/access/token/2/new"
   629  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
   630  	if err != nil {
   631  		t.Fatalf("failed to create test request: %v", err)
   632  	}
   633  	client2.Credential = StaticCredential(uri2.Host, Credential{
   634  		AccessToken: accessToken2,
   635  	})
   636  	resp2, err = client2.Do(req2)
   637  	if err != nil {
   638  		t.Fatalf("Client.Do() error = %v", err)
   639  	}
   640  	if resp2.StatusCode != http.StatusOK {
   641  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
   642  	}
   643  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
   644  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
   645  	}
   646  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
   647  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
   648  	}
   649  }
   650  
   651  func TestClient_Do_Bearer_Auth(t *testing.T) {
   652  	username := "test_user"
   653  	password := "test_password"
   654  	accessToken := "test/access/token"
   655  	var requestCount, wantRequestCount int64
   656  	var successCount, wantSuccessCount int64
   657  	var authCount, wantAuthCount int64
   658  	var service string
   659  	scopes := []string{
   660  		"repository:dst:pull,push",
   661  		"repository:src:pull",
   662  	}
   663  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   664  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   665  			t.Error("unexecuted attempt of authorization service")
   666  			w.WriteHeader(http.StatusUnauthorized)
   667  			return
   668  		}
   669  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
   670  		if auth := r.Header.Get("Authorization"); auth != header {
   671  			t.Errorf("unexpected auth: got %s, want %s", auth, header)
   672  			w.WriteHeader(http.StatusUnauthorized)
   673  			return
   674  		}
   675  		if got := r.URL.Query().Get("service"); got != service {
   676  			t.Errorf("unexpected service: got %s, want %s", got, service)
   677  			w.WriteHeader(http.StatusUnauthorized)
   678  			return
   679  		}
   680  		if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes) {
   681  			t.Errorf("unexpected scope: got %s, want %s", got, scopes)
   682  			w.WriteHeader(http.StatusUnauthorized)
   683  			return
   684  		}
   685  
   686  		atomic.AddInt64(&authCount, 1)
   687  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
   688  			t.Errorf("failed to write %q: %v", r.URL, err)
   689  		}
   690  	}))
   691  	defer as.Close()
   692  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   693  		atomic.AddInt64(&requestCount, 1)
   694  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   695  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   696  			w.WriteHeader(http.StatusNotFound)
   697  			return
   698  		}
   699  		header := "Bearer " + accessToken
   700  		if auth := r.Header.Get("Authorization"); auth != header {
   701  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
   702  			w.Header().Set("Www-Authenticate", challenge)
   703  			w.WriteHeader(http.StatusUnauthorized)
   704  			return
   705  		}
   706  		atomic.AddInt64(&successCount, 1)
   707  	}))
   708  	defer ts.Close()
   709  	uri, err := url.Parse(ts.URL)
   710  	if err != nil {
   711  		t.Fatalf("invalid test http server: %v", err)
   712  	}
   713  	service = uri.Host
   714  
   715  	client := &Client{
   716  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   717  			if reg != uri.Host {
   718  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   719  				t.Error(err)
   720  				return EmptyCredential, err
   721  			}
   722  			return Credential{
   723  				Username: username,
   724  				Password: password,
   725  			}, nil
   726  		},
   727  	}
   728  
   729  	// first request
   730  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   731  	if err != nil {
   732  		t.Fatalf("failed to create test request: %v", err)
   733  	}
   734  	resp, err := client.Do(req)
   735  	if err != nil {
   736  		t.Fatalf("Client.Do() error = %v", err)
   737  	}
   738  	if resp.StatusCode != http.StatusOK {
   739  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   740  	}
   741  	if wantRequestCount += 2; requestCount != wantRequestCount {
   742  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   743  	}
   744  	if wantSuccessCount++; successCount != wantSuccessCount {
   745  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   746  	}
   747  	if wantAuthCount++; authCount != wantAuthCount {
   748  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   749  	}
   750  
   751  	// credential change
   752  	username = "test_user2"
   753  	password = "test_password2"
   754  	accessToken = "test/access/token/2"
   755  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   756  	if err != nil {
   757  		t.Fatalf("failed to create test request: %v", err)
   758  	}
   759  	resp, err = client.Do(req)
   760  	if err != nil {
   761  		t.Fatalf("Client.Do() error = %v", err)
   762  	}
   763  	if resp.StatusCode != http.StatusOK {
   764  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   765  	}
   766  	if wantRequestCount += 2; requestCount != wantRequestCount {
   767  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   768  	}
   769  	if wantSuccessCount++; successCount != wantSuccessCount {
   770  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   771  	}
   772  	if wantAuthCount++; authCount != wantAuthCount {
   773  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   774  	}
   775  }
   776  
   777  func TestClient_Do_Bearer_Auth_Cached(t *testing.T) {
   778  	username := "test_user"
   779  	password := "test_password"
   780  	accessToken := "test/access/token"
   781  	var requestCount, wantRequestCount int64
   782  	var successCount, wantSuccessCount int64
   783  	var authCount, wantAuthCount int64
   784  	var service string
   785  	scopes := []string{
   786  		"repository:dst:pull,push",
   787  		"repository:src:pull",
   788  	}
   789  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   790  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   791  			t.Error("unexecuted attempt of authorization service")
   792  			w.WriteHeader(http.StatusUnauthorized)
   793  			return
   794  		}
   795  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
   796  		if auth := r.Header.Get("Authorization"); auth != header {
   797  			t.Errorf("unexpected auth: got %s, want %s", auth, header)
   798  			w.WriteHeader(http.StatusUnauthorized)
   799  			return
   800  		}
   801  		if got := r.URL.Query().Get("service"); got != service {
   802  			t.Errorf("unexpected service: got %s, want %s", got, service)
   803  			w.WriteHeader(http.StatusUnauthorized)
   804  			return
   805  		}
   806  		if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes) {
   807  			t.Errorf("unexpected scope: got %s, want %s", got, scopes)
   808  			w.WriteHeader(http.StatusUnauthorized)
   809  			return
   810  		}
   811  
   812  		atomic.AddInt64(&authCount, 1)
   813  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
   814  			t.Errorf("failed to write %q: %v", r.URL, err)
   815  		}
   816  	}))
   817  	defer as.Close()
   818  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   819  		atomic.AddInt64(&requestCount, 1)
   820  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   821  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   822  			w.WriteHeader(http.StatusNotFound)
   823  			return
   824  		}
   825  		header := "Bearer " + accessToken
   826  		if auth := r.Header.Get("Authorization"); auth != header {
   827  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
   828  			w.Header().Set("Www-Authenticate", challenge)
   829  			w.WriteHeader(http.StatusUnauthorized)
   830  			return
   831  		}
   832  		atomic.AddInt64(&successCount, 1)
   833  	}))
   834  	defer ts.Close()
   835  	uri, err := url.Parse(ts.URL)
   836  	if err != nil {
   837  		t.Fatalf("invalid test http server: %v", err)
   838  	}
   839  	service = uri.Host
   840  
   841  	client := &Client{
   842  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   843  			if reg != uri.Host {
   844  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   845  				t.Error(err)
   846  				return EmptyCredential, err
   847  			}
   848  			return Credential{
   849  				Username: username,
   850  				Password: password,
   851  			}, nil
   852  		},
   853  		Cache: NewCache(),
   854  	}
   855  
   856  	// first request
   857  	ctx := WithScopes(context.Background(), scopes...)
   858  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   859  	if err != nil {
   860  		t.Fatalf("failed to create test request: %v", err)
   861  	}
   862  	resp, err := client.Do(req)
   863  	if err != nil {
   864  		t.Fatalf("Client.Do() error = %v", err)
   865  	}
   866  	if resp.StatusCode != http.StatusOK {
   867  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   868  	}
   869  	if wantRequestCount += 2; requestCount != wantRequestCount {
   870  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   871  	}
   872  	if wantSuccessCount++; successCount != wantSuccessCount {
   873  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   874  	}
   875  	if wantAuthCount++; authCount != wantAuthCount {
   876  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   877  	}
   878  
   879  	// repeated request
   880  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   881  	if err != nil {
   882  		t.Fatalf("failed to create test request: %v", err)
   883  	}
   884  	resp, err = client.Do(req)
   885  	if err != nil {
   886  		t.Fatalf("Client.Do() error = %v", err)
   887  	}
   888  	if resp.StatusCode != http.StatusOK {
   889  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   890  	}
   891  	if wantRequestCount++; requestCount != wantRequestCount {
   892  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   893  	}
   894  	if wantSuccessCount++; successCount != wantSuccessCount {
   895  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   896  	}
   897  	if authCount != wantAuthCount {
   898  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   899  	}
   900  
   901  	// credential change
   902  	username = "test_user2"
   903  	password = "test_password2"
   904  	accessToken = "test/access/token/2"
   905  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   906  	if err != nil {
   907  		t.Fatalf("failed to create test request: %v", err)
   908  	}
   909  	resp, err = client.Do(req)
   910  	if err != nil {
   911  		t.Fatalf("Client.Do() error = %v", err)
   912  	}
   913  	if resp.StatusCode != http.StatusOK {
   914  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   915  	}
   916  	if wantRequestCount += 2; requestCount != wantRequestCount {
   917  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   918  	}
   919  	if wantSuccessCount++; successCount != wantSuccessCount {
   920  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   921  	}
   922  	if wantAuthCount++; authCount != wantAuthCount {
   923  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   924  	}
   925  }
   926  
   927  func TestClient_Do_Bearer_Auth_Cached_PerHost(t *testing.T) {
   928  	// set up server 1
   929  	username1 := "test_user1"
   930  	password1 := "test_password1"
   931  	accessToken1 := "test/access/token/1"
   932  	var requestCount1, wantRequestCount1 int64
   933  	var successCount1, wantSuccessCount1 int64
   934  	var authCount1, wantAuthCount1 int64
   935  	var service1 string
   936  	scopes1 := []string{
   937  		"repository:src:pull",
   938  	}
   939  	as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   940  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   941  			t.Error("unexecuted attempt of authorization service")
   942  			w.WriteHeader(http.StatusUnauthorized)
   943  			return
   944  		}
   945  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username1+":"+password1))
   946  		if auth := r.Header.Get("Authorization"); auth != header {
   947  			t.Errorf("unexpected auth: got %s, want %s", auth, header)
   948  			w.WriteHeader(http.StatusUnauthorized)
   949  			return
   950  		}
   951  		if got := r.URL.Query().Get("service"); got != service1 {
   952  			t.Errorf("unexpected service: got %s, want %s", got, service1)
   953  			w.WriteHeader(http.StatusUnauthorized)
   954  			return
   955  		}
   956  		if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes1) {
   957  			t.Errorf("unexpected scope: got %s, want %s", got, scopes1)
   958  			w.WriteHeader(http.StatusUnauthorized)
   959  			return
   960  		}
   961  
   962  		atomic.AddInt64(&authCount1, 1)
   963  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil {
   964  			t.Errorf("failed to write %q: %v", r.URL, err)
   965  		}
   966  	}))
   967  	defer as1.Close()
   968  	ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   969  		atomic.AddInt64(&requestCount1, 1)
   970  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   971  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   972  			w.WriteHeader(http.StatusNotFound)
   973  			return
   974  		}
   975  		header := "Bearer " + accessToken1
   976  		if auth := r.Header.Get("Authorization"); auth != header {
   977  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, strings.Join(scopes1, " "))
   978  			w.Header().Set("Www-Authenticate", challenge)
   979  			w.WriteHeader(http.StatusUnauthorized)
   980  			return
   981  		}
   982  		atomic.AddInt64(&successCount1, 1)
   983  	}))
   984  	defer ts1.Close()
   985  	uri1, err := url.Parse(ts1.URL)
   986  	if err != nil {
   987  		t.Fatalf("invalid test http server: %v", err)
   988  	}
   989  	service1 = uri1.Host
   990  	client1 := &Client{
   991  		Credential: StaticCredential(uri1.Host, Credential{
   992  			Username: username1,
   993  			Password: password1,
   994  		}),
   995  		Cache: NewCache(),
   996  	}
   997  
   998  	// set up server 2
   999  	username2 := "test_user2"
  1000  	password2 := "test_password2"
  1001  	accessToken2 := "test/access/token/1"
  1002  	var requestCount2, wantRequestCount2 int64
  1003  	var successCount2, wantSuccessCount2 int64
  1004  	var authCount2, wantAuthCount2 int64
  1005  	var service2 string
  1006  	scopes2 := []string{
  1007  		"repository:dst:pull,push",
  1008  	}
  1009  	as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1010  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1011  			t.Error("unexecuted attempt of authorization service")
  1012  			w.WriteHeader(http.StatusUnauthorized)
  1013  			return
  1014  		}
  1015  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username2+":"+password2))
  1016  		if auth := r.Header.Get("Authorization"); auth != header {
  1017  			t.Errorf("unexpected auth: got %s, want %s", auth, header)
  1018  			w.WriteHeader(http.StatusUnauthorized)
  1019  			return
  1020  		}
  1021  		if got := r.URL.Query().Get("service"); got != service2 {
  1022  			t.Errorf("unexpected service: got %s, want %s", got, service2)
  1023  			w.WriteHeader(http.StatusUnauthorized)
  1024  			return
  1025  		}
  1026  		if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes2) {
  1027  			t.Errorf("unexpected scope: got %s, want %s", got, scopes2)
  1028  			w.WriteHeader(http.StatusUnauthorized)
  1029  			return
  1030  		}
  1031  
  1032  		atomic.AddInt64(&authCount2, 1)
  1033  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil {
  1034  			t.Errorf("failed to write %q: %v", r.URL, err)
  1035  		}
  1036  	}))
  1037  	defer as1.Close()
  1038  	ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1039  		atomic.AddInt64(&requestCount2, 1)
  1040  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1041  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1042  			w.WriteHeader(http.StatusNotFound)
  1043  			return
  1044  		}
  1045  		header := "Bearer " + accessToken2
  1046  		if auth := r.Header.Get("Authorization"); auth != header {
  1047  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, strings.Join(scopes2, " "))
  1048  			w.Header().Set("Www-Authenticate", challenge)
  1049  			w.WriteHeader(http.StatusUnauthorized)
  1050  			return
  1051  		}
  1052  		atomic.AddInt64(&successCount2, 1)
  1053  	}))
  1054  	defer ts2.Close()
  1055  	uri2, err := url.Parse(ts2.URL)
  1056  	if err != nil {
  1057  		t.Fatalf("invalid test http server: %v", err)
  1058  	}
  1059  	service2 = uri2.Host
  1060  	client2 := &Client{
  1061  		Credential: StaticCredential(uri2.Host, Credential{
  1062  			Username: username2,
  1063  			Password: password2,
  1064  		}),
  1065  		Cache: NewCache(),
  1066  	}
  1067  
  1068  	ctx := context.Background()
  1069  	ctx = WithScopesForHost(ctx, uri1.Host, scopes1...)
  1070  	ctx = WithScopesForHost(ctx, uri2.Host, scopes2...)
  1071  	// first request to server 1
  1072  	req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  1073  	if err != nil {
  1074  		t.Fatalf("failed to create test request: %v", err)
  1075  	}
  1076  	resp1, err := client1.Do(req1)
  1077  	if err != nil {
  1078  		t.Fatalf("Client.Do() error = %v", err)
  1079  	}
  1080  	if resp1.StatusCode != http.StatusOK {
  1081  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  1082  	}
  1083  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  1084  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  1085  	}
  1086  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  1087  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  1088  	}
  1089  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  1090  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  1091  	}
  1092  
  1093  	// first request to server 2
  1094  	req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  1095  	if err != nil {
  1096  		t.Fatalf("failed to create test request: %v", err)
  1097  	}
  1098  	resp2, err := client2.Do(req2)
  1099  	if err != nil {
  1100  		t.Fatalf("Client.Do() error = %v", err)
  1101  	}
  1102  	if resp2.StatusCode != http.StatusOK {
  1103  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  1104  	}
  1105  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  1106  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  1107  	}
  1108  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  1109  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  1110  	}
  1111  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  1112  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  1113  	}
  1114  
  1115  	// repeated request to server 1
  1116  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  1117  	if err != nil {
  1118  		t.Fatalf("failed to create test request: %v", err)
  1119  	}
  1120  	resp1, err = client1.Do(req1)
  1121  	if err != nil {
  1122  		t.Fatalf("Client.Do() error = %v", err)
  1123  	}
  1124  	if resp1.StatusCode != http.StatusOK {
  1125  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  1126  	}
  1127  	if wantRequestCount1++; requestCount1 != wantRequestCount1 {
  1128  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  1129  	}
  1130  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  1131  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  1132  	}
  1133  	if authCount1 != wantAuthCount1 {
  1134  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  1135  	}
  1136  
  1137  	// repeated request to server 2
  1138  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  1139  	if err != nil {
  1140  		t.Fatalf("failed to create test request: %v", err)
  1141  	}
  1142  	resp2, err = client2.Do(req2)
  1143  	if err != nil {
  1144  		t.Fatalf("Client.Do() error = %v", err)
  1145  	}
  1146  	if resp2.StatusCode != http.StatusOK {
  1147  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  1148  	}
  1149  	if wantRequestCount2++; requestCount2 != wantRequestCount2 {
  1150  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  1151  	}
  1152  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  1153  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  1154  	}
  1155  	if authCount2 != wantAuthCount2 {
  1156  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  1157  	}
  1158  
  1159  	// credential change for server 1
  1160  	username1 = "test_user1_new"
  1161  	password1 = "test_password1_new"
  1162  	accessToken1 = "test/access/token/1/new"
  1163  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  1164  	if err != nil {
  1165  		t.Fatalf("failed to create test request: %v", err)
  1166  	}
  1167  	client1.Credential = StaticCredential(uri1.Host, Credential{
  1168  		Username: username1,
  1169  		Password: password1,
  1170  	})
  1171  	resp1, err = client1.Do(req1)
  1172  	if err != nil {
  1173  		t.Fatalf("Client.Do() error = %v", err)
  1174  	}
  1175  	if resp1.StatusCode != http.StatusOK {
  1176  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  1177  	}
  1178  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  1179  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  1180  	}
  1181  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  1182  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  1183  	}
  1184  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  1185  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  1186  	}
  1187  
  1188  	// credential change for server 2
  1189  	username2 = "test_user2_new"
  1190  	password2 = "test_password2_new"
  1191  	accessToken2 = "test/access/token/2/new"
  1192  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  1193  	if err != nil {
  1194  		t.Fatalf("failed to create test request: %v", err)
  1195  	}
  1196  	client2.Credential = StaticCredential(uri2.Host, Credential{
  1197  		Username: username2,
  1198  		Password: password2,
  1199  	})
  1200  	resp2, err = client2.Do(req2)
  1201  	if err != nil {
  1202  		t.Fatalf("Client.Do() error = %v", err)
  1203  	}
  1204  	if resp2.StatusCode != http.StatusOK {
  1205  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  1206  	}
  1207  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  1208  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  1209  	}
  1210  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  1211  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  1212  	}
  1213  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  1214  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  1215  	}
  1216  }
  1217  
  1218  func TestClient_Do_Bearer_OAuth2_Password(t *testing.T) {
  1219  	username := "test_user"
  1220  	password := "test_password"
  1221  	accessToken := "test/access/token"
  1222  	var requestCount, wantRequestCount int64
  1223  	var successCount, wantSuccessCount int64
  1224  	var authCount, wantAuthCount int64
  1225  	var service string
  1226  	scopes := []string{
  1227  		"repository:dst:pull,push",
  1228  		"repository:src:pull",
  1229  	}
  1230  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1231  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1232  			t.Error("unexecuted attempt of authorization service")
  1233  			w.WriteHeader(http.StatusUnauthorized)
  1234  			return
  1235  		}
  1236  		if err := r.ParseForm(); err != nil {
  1237  			t.Errorf("failed to parse form: %v", err)
  1238  			w.WriteHeader(http.StatusUnauthorized)
  1239  			return
  1240  		}
  1241  		if got := r.PostForm.Get("grant_type"); got != "password" {
  1242  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
  1243  			w.WriteHeader(http.StatusUnauthorized)
  1244  			return
  1245  		}
  1246  		if got := r.PostForm.Get("service"); got != service {
  1247  			t.Errorf("unexpected service: %v, want %v", got, service)
  1248  			w.WriteHeader(http.StatusUnauthorized)
  1249  			return
  1250  		}
  1251  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1252  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1253  			w.WriteHeader(http.StatusUnauthorized)
  1254  			return
  1255  		}
  1256  		scope := strings.Join(scopes, " ")
  1257  		if got := r.PostForm.Get("scope"); got != scope {
  1258  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1259  			w.WriteHeader(http.StatusUnauthorized)
  1260  			return
  1261  		}
  1262  		if got := r.PostForm.Get("username"); got != username {
  1263  			t.Errorf("unexpected username: %v, want %v", got, username)
  1264  			w.WriteHeader(http.StatusUnauthorized)
  1265  			return
  1266  		}
  1267  		if got := r.PostForm.Get("password"); got != password {
  1268  			t.Errorf("unexpected password: %v, want %v", got, password)
  1269  			w.WriteHeader(http.StatusUnauthorized)
  1270  			return
  1271  		}
  1272  
  1273  		atomic.AddInt64(&authCount, 1)
  1274  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1275  			t.Errorf("failed to write %q: %v", r.URL, err)
  1276  		}
  1277  	}))
  1278  	defer as.Close()
  1279  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1280  		atomic.AddInt64(&requestCount, 1)
  1281  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1282  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1283  			w.WriteHeader(http.StatusNotFound)
  1284  			return
  1285  		}
  1286  		header := "Bearer " + accessToken
  1287  		if auth := r.Header.Get("Authorization"); auth != header {
  1288  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  1289  			w.Header().Set("Www-Authenticate", challenge)
  1290  			w.WriteHeader(http.StatusUnauthorized)
  1291  			return
  1292  		}
  1293  		atomic.AddInt64(&successCount, 1)
  1294  	}))
  1295  	defer ts.Close()
  1296  	uri, err := url.Parse(ts.URL)
  1297  	if err != nil {
  1298  		t.Fatalf("invalid test http server: %v", err)
  1299  	}
  1300  	service = uri.Host
  1301  
  1302  	client := &Client{
  1303  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1304  			if reg != uri.Host {
  1305  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1306  				t.Error(err)
  1307  				return EmptyCredential, err
  1308  			}
  1309  			return Credential{
  1310  				Username: username,
  1311  				Password: password,
  1312  			}, nil
  1313  		},
  1314  		ForceAttemptOAuth2: true,
  1315  	}
  1316  
  1317  	// first request
  1318  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  1319  	if err != nil {
  1320  		t.Fatalf("failed to create test request: %v", err)
  1321  	}
  1322  	resp, err := client.Do(req)
  1323  	if err != nil {
  1324  		t.Fatalf("Client.Do() error = %v", err)
  1325  	}
  1326  	if resp.StatusCode != http.StatusOK {
  1327  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1328  	}
  1329  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1330  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1331  	}
  1332  	if wantSuccessCount++; successCount != wantSuccessCount {
  1333  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1334  	}
  1335  	if wantAuthCount++; authCount != wantAuthCount {
  1336  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1337  	}
  1338  
  1339  	// credential change
  1340  	username = "test_user2"
  1341  	password = "test_password2"
  1342  	accessToken = "test/access/token/2"
  1343  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
  1344  	if err != nil {
  1345  		t.Fatalf("failed to create test request: %v", err)
  1346  	}
  1347  	resp, err = client.Do(req)
  1348  	if err != nil {
  1349  		t.Fatalf("Client.Do() error = %v", err)
  1350  	}
  1351  	if resp.StatusCode != http.StatusOK {
  1352  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1353  	}
  1354  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1355  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1356  	}
  1357  	if wantSuccessCount++; successCount != wantSuccessCount {
  1358  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1359  	}
  1360  	if wantAuthCount++; authCount != wantAuthCount {
  1361  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1362  	}
  1363  }
  1364  
  1365  func TestClient_Do_Bearer_OAuth2_Password_Cached(t *testing.T) {
  1366  	username := "test_user"
  1367  	password := "test_password"
  1368  	accessToken := "test/access/token"
  1369  	var requestCount, wantRequestCount int64
  1370  	var successCount, wantSuccessCount int64
  1371  	var authCount, wantAuthCount int64
  1372  	var service string
  1373  	scopes := []string{
  1374  		"repository:dst:pull,push",
  1375  		"repository:src:pull",
  1376  	}
  1377  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1378  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1379  			t.Error("unexecuted attempt of authorization service")
  1380  			w.WriteHeader(http.StatusUnauthorized)
  1381  			return
  1382  		}
  1383  		if err := r.ParseForm(); err != nil {
  1384  			t.Errorf("failed to parse form: %v", err)
  1385  			w.WriteHeader(http.StatusUnauthorized)
  1386  			return
  1387  		}
  1388  		if got := r.PostForm.Get("grant_type"); got != "password" {
  1389  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
  1390  			w.WriteHeader(http.StatusUnauthorized)
  1391  			return
  1392  		}
  1393  		if got := r.PostForm.Get("service"); got != service {
  1394  			t.Errorf("unexpected service: %v, want %v", got, service)
  1395  			w.WriteHeader(http.StatusUnauthorized)
  1396  			return
  1397  		}
  1398  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1399  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1400  			w.WriteHeader(http.StatusUnauthorized)
  1401  			return
  1402  		}
  1403  		scope := strings.Join(scopes, " ")
  1404  		if got := r.PostForm.Get("scope"); got != scope {
  1405  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1406  			w.WriteHeader(http.StatusUnauthorized)
  1407  			return
  1408  		}
  1409  		if got := r.PostForm.Get("username"); got != username {
  1410  			t.Errorf("unexpected username: %v, want %v", got, username)
  1411  			w.WriteHeader(http.StatusUnauthorized)
  1412  			return
  1413  		}
  1414  		if got := r.PostForm.Get("password"); got != password {
  1415  			t.Errorf("unexpected password: %v, want %v", got, password)
  1416  			w.WriteHeader(http.StatusUnauthorized)
  1417  			return
  1418  		}
  1419  
  1420  		atomic.AddInt64(&authCount, 1)
  1421  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1422  			t.Errorf("failed to write %q: %v", r.URL, err)
  1423  		}
  1424  	}))
  1425  	defer as.Close()
  1426  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1427  		atomic.AddInt64(&requestCount, 1)
  1428  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1429  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1430  			w.WriteHeader(http.StatusNotFound)
  1431  			return
  1432  		}
  1433  		header := "Bearer " + accessToken
  1434  		if auth := r.Header.Get("Authorization"); auth != header {
  1435  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  1436  			w.Header().Set("Www-Authenticate", challenge)
  1437  			w.WriteHeader(http.StatusUnauthorized)
  1438  			return
  1439  		}
  1440  		atomic.AddInt64(&successCount, 1)
  1441  	}))
  1442  	defer ts.Close()
  1443  	uri, err := url.Parse(ts.URL)
  1444  	if err != nil {
  1445  		t.Fatalf("invalid test http server: %v", err)
  1446  	}
  1447  	service = uri.Host
  1448  
  1449  	client := &Client{
  1450  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1451  			if reg != uri.Host {
  1452  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1453  				t.Error(err)
  1454  				return EmptyCredential, err
  1455  			}
  1456  			return Credential{
  1457  				Username: username,
  1458  				Password: password,
  1459  			}, nil
  1460  		},
  1461  		ForceAttemptOAuth2: true,
  1462  		Cache:              NewCache(),
  1463  	}
  1464  
  1465  	// first request
  1466  	ctx := WithScopes(context.Background(), scopes...)
  1467  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1468  	if err != nil {
  1469  		t.Fatalf("failed to create test request: %v", err)
  1470  	}
  1471  	resp, err := client.Do(req)
  1472  	if err != nil {
  1473  		t.Fatalf("Client.Do() error = %v", err)
  1474  	}
  1475  	if resp.StatusCode != http.StatusOK {
  1476  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1477  	}
  1478  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1479  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1480  	}
  1481  	if wantSuccessCount++; successCount != wantSuccessCount {
  1482  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1483  	}
  1484  	if wantAuthCount++; authCount != wantAuthCount {
  1485  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1486  	}
  1487  
  1488  	// repeated request
  1489  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1490  	if err != nil {
  1491  		t.Fatalf("failed to create test request: %v", err)
  1492  	}
  1493  	resp, err = client.Do(req)
  1494  	if err != nil {
  1495  		t.Fatalf("Client.Do() error = %v", err)
  1496  	}
  1497  	if resp.StatusCode != http.StatusOK {
  1498  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1499  	}
  1500  	if wantRequestCount++; requestCount != wantRequestCount {
  1501  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1502  	}
  1503  	if wantSuccessCount++; successCount != wantSuccessCount {
  1504  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1505  	}
  1506  	if authCount != wantAuthCount {
  1507  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1508  	}
  1509  
  1510  	// credential change
  1511  	username = "test_user2"
  1512  	password = "test_password2"
  1513  	accessToken = "test/access/token/2"
  1514  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1515  	if err != nil {
  1516  		t.Fatalf("failed to create test request: %v", err)
  1517  	}
  1518  	resp, err = client.Do(req)
  1519  	if err != nil {
  1520  		t.Fatalf("Client.Do() error = %v", err)
  1521  	}
  1522  	if resp.StatusCode != http.StatusOK {
  1523  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1524  	}
  1525  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1526  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1527  	}
  1528  	if wantSuccessCount++; successCount != wantSuccessCount {
  1529  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1530  	}
  1531  	if wantAuthCount++; authCount != wantAuthCount {
  1532  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1533  	}
  1534  }
  1535  
  1536  func TestClient_Do_Bearer_OAuth2_Password_Cached_PerHost(t *testing.T) {
  1537  	// set up server 1
  1538  	username1 := "test_user1"
  1539  	password1 := "test_password1"
  1540  	accessToken1 := "test/access/token/1"
  1541  	var requestCount1, wantRequestCount1 int64
  1542  	var successCount1, wantSuccessCount1 int64
  1543  	var authCount1, wantAuthCount1 int64
  1544  	var service1 string
  1545  	scopes1 := []string{
  1546  		"repository:src:pull",
  1547  	}
  1548  	as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1549  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1550  			t.Error("unexecuted attempt of authorization service")
  1551  			w.WriteHeader(http.StatusUnauthorized)
  1552  			return
  1553  		}
  1554  		if err := r.ParseForm(); err != nil {
  1555  			t.Errorf("failed to parse form: %v", err)
  1556  			w.WriteHeader(http.StatusUnauthorized)
  1557  			return
  1558  		}
  1559  		if got := r.PostForm.Get("grant_type"); got != "password" {
  1560  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
  1561  			w.WriteHeader(http.StatusUnauthorized)
  1562  			return
  1563  		}
  1564  		if got := r.PostForm.Get("service"); got != service1 {
  1565  			t.Errorf("unexpected service: %v, want %v", got, service1)
  1566  			w.WriteHeader(http.StatusUnauthorized)
  1567  			return
  1568  		}
  1569  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1570  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1571  			w.WriteHeader(http.StatusUnauthorized)
  1572  			return
  1573  		}
  1574  		scope := strings.Join(scopes1, " ")
  1575  		if got := r.PostForm.Get("scope"); got != scope {
  1576  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1577  			w.WriteHeader(http.StatusUnauthorized)
  1578  			return
  1579  		}
  1580  		if got := r.PostForm.Get("username"); got != username1 {
  1581  			t.Errorf("unexpected username: %v, want %v", got, username1)
  1582  			w.WriteHeader(http.StatusUnauthorized)
  1583  			return
  1584  		}
  1585  		if got := r.PostForm.Get("password"); got != password1 {
  1586  			t.Errorf("unexpected password: %v, want %v", got, password1)
  1587  			w.WriteHeader(http.StatusUnauthorized)
  1588  			return
  1589  		}
  1590  
  1591  		atomic.AddInt64(&authCount1, 1)
  1592  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil {
  1593  			t.Errorf("failed to write %q: %v", r.URL, err)
  1594  		}
  1595  	}))
  1596  	defer as1.Close()
  1597  	ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1598  		atomic.AddInt64(&requestCount1, 1)
  1599  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1600  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1601  			w.WriteHeader(http.StatusNotFound)
  1602  			return
  1603  		}
  1604  		header := "Bearer " + accessToken1
  1605  		if auth := r.Header.Get("Authorization"); auth != header {
  1606  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, strings.Join(scopes1, " "))
  1607  			w.Header().Set("Www-Authenticate", challenge)
  1608  			w.WriteHeader(http.StatusUnauthorized)
  1609  			return
  1610  		}
  1611  		atomic.AddInt64(&successCount1, 1)
  1612  	}))
  1613  	defer ts1.Close()
  1614  	uri1, err := url.Parse(ts1.URL)
  1615  	if err != nil {
  1616  		t.Fatalf("invalid test http server: %v", err)
  1617  	}
  1618  	service1 = uri1.Host
  1619  	client1 := &Client{
  1620  		Credential: StaticCredential(uri1.Host, Credential{
  1621  			Username: username1,
  1622  			Password: password1,
  1623  		}),
  1624  		ForceAttemptOAuth2: true,
  1625  		Cache:              NewCache(),
  1626  	}
  1627  	// set up server 2
  1628  	username2 := "test_user2"
  1629  	password2 := "test_password2"
  1630  	accessToken2 := "test/access/token/2"
  1631  	var requestCount2, wantRequestCount2 int64
  1632  	var successCount2, wantSuccessCount2 int64
  1633  	var authCount2, wantAuthCount2 int64
  1634  	var service2 string
  1635  	scopes2 := []string{
  1636  		"repository:dst:pull,push",
  1637  	}
  1638  	as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1639  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1640  			t.Error("unexecuted attempt of authorization service")
  1641  			w.WriteHeader(http.StatusUnauthorized)
  1642  			return
  1643  		}
  1644  		if err := r.ParseForm(); err != nil {
  1645  			t.Errorf("failed to parse form: %v", err)
  1646  			w.WriteHeader(http.StatusUnauthorized)
  1647  			return
  1648  		}
  1649  		if got := r.PostForm.Get("grant_type"); got != "password" {
  1650  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
  1651  			w.WriteHeader(http.StatusUnauthorized)
  1652  			return
  1653  		}
  1654  		if got := r.PostForm.Get("service"); got != service2 {
  1655  			t.Errorf("unexpected service: %v, want %v", got, service2)
  1656  			w.WriteHeader(http.StatusUnauthorized)
  1657  			return
  1658  		}
  1659  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1660  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1661  			w.WriteHeader(http.StatusUnauthorized)
  1662  			return
  1663  		}
  1664  		scope := strings.Join(scopes2, " ")
  1665  		if got := r.PostForm.Get("scope"); got != scope {
  1666  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1667  			w.WriteHeader(http.StatusUnauthorized)
  1668  			return
  1669  		}
  1670  		if got := r.PostForm.Get("username"); got != username2 {
  1671  			t.Errorf("unexpected username: %v, want %v", got, username2)
  1672  			w.WriteHeader(http.StatusUnauthorized)
  1673  			return
  1674  		}
  1675  		if got := r.PostForm.Get("password"); got != password2 {
  1676  			t.Errorf("unexpected password: %v, want %v", got, password2)
  1677  			w.WriteHeader(http.StatusUnauthorized)
  1678  			return
  1679  		}
  1680  
  1681  		atomic.AddInt64(&authCount2, 1)
  1682  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil {
  1683  			t.Errorf("failed to write %q: %v", r.URL, err)
  1684  		}
  1685  	}))
  1686  	defer as2.Close()
  1687  	ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1688  		atomic.AddInt64(&requestCount2, 1)
  1689  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1690  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1691  			w.WriteHeader(http.StatusNotFound)
  1692  			return
  1693  		}
  1694  		header := "Bearer " + accessToken2
  1695  		if auth := r.Header.Get("Authorization"); auth != header {
  1696  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, strings.Join(scopes2, " "))
  1697  			w.Header().Set("Www-Authenticate", challenge)
  1698  			w.WriteHeader(http.StatusUnauthorized)
  1699  			return
  1700  		}
  1701  		atomic.AddInt64(&successCount2, 1)
  1702  	}))
  1703  	defer ts2.Close()
  1704  	uri2, err := url.Parse(ts2.URL)
  1705  	if err != nil {
  1706  		t.Fatalf("invalid test http server: %v", err)
  1707  	}
  1708  	service2 = uri2.Host
  1709  	client2 := &Client{
  1710  		Credential: StaticCredential(uri2.Host, Credential{
  1711  			Username: username2,
  1712  			Password: password2,
  1713  		}),
  1714  		ForceAttemptOAuth2: true,
  1715  		Cache:              NewCache(),
  1716  	}
  1717  
  1718  	ctx := context.Background()
  1719  	ctx = WithScopesForHost(ctx, uri1.Host, scopes1...)
  1720  	ctx = WithScopesForHost(ctx, uri2.Host, scopes2...)
  1721  	// first request to server 1
  1722  	req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  1723  	if err != nil {
  1724  		t.Fatalf("failed to create test request: %v", err)
  1725  	}
  1726  	resp1, err := client1.Do(req1)
  1727  	if err != nil {
  1728  		t.Fatalf("Client.Do() error = %v", err)
  1729  	}
  1730  	if resp1.StatusCode != http.StatusOK {
  1731  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  1732  	}
  1733  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  1734  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  1735  	}
  1736  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  1737  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  1738  	}
  1739  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  1740  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  1741  	}
  1742  	// first request to server 2
  1743  	req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  1744  	if err != nil {
  1745  		t.Fatalf("failed to create test request: %v", err)
  1746  	}
  1747  	resp2, err := client2.Do(req2)
  1748  	if err != nil {
  1749  		t.Fatalf("Client.Do() error = %v", err)
  1750  	}
  1751  	if resp2.StatusCode != http.StatusOK {
  1752  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  1753  	}
  1754  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  1755  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  1756  	}
  1757  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  1758  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  1759  	}
  1760  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  1761  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  1762  	}
  1763  
  1764  	// repeated request to server 1
  1765  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  1766  	if err != nil {
  1767  		t.Fatalf("failed to create test request: %v", err)
  1768  	}
  1769  	resp1, err = client1.Do(req1)
  1770  	if err != nil {
  1771  		t.Fatalf("Client.Do() error = %v", err)
  1772  	}
  1773  	if resp1.StatusCode != http.StatusOK {
  1774  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  1775  	}
  1776  	if wantRequestCount1++; requestCount1 != wantRequestCount1 {
  1777  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  1778  	}
  1779  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  1780  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  1781  	}
  1782  	if authCount1 != wantAuthCount1 {
  1783  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  1784  	}
  1785  	// repeated request to server 2
  1786  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  1787  	if err != nil {
  1788  		t.Fatalf("failed to create test request: %v", err)
  1789  	}
  1790  	resp2, err = client2.Do(req2)
  1791  	if err != nil {
  1792  		t.Fatalf("Client.Do() error = %v", err)
  1793  	}
  1794  	if resp2.StatusCode != http.StatusOK {
  1795  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  1796  	}
  1797  	if wantRequestCount2++; requestCount2 != wantRequestCount2 {
  1798  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  1799  	}
  1800  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  1801  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  1802  	}
  1803  	if authCount2 != wantAuthCount2 {
  1804  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  1805  	}
  1806  
  1807  	// credential change for server 1
  1808  	username1 = "test_user1_new"
  1809  	password1 = "test_password1_new"
  1810  	accessToken1 = "test/access/token/1/new"
  1811  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  1812  	if err != nil {
  1813  		t.Fatalf("failed to create test request: %v", err)
  1814  	}
  1815  	client1.Credential = StaticCredential(uri1.Host, Credential{
  1816  		Username: username1,
  1817  		Password: password1,
  1818  	})
  1819  	resp1, err = client1.Do(req1)
  1820  	if err != nil {
  1821  		t.Fatalf("Client.Do() error = %v", err)
  1822  	}
  1823  	if resp1.StatusCode != http.StatusOK {
  1824  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  1825  	}
  1826  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  1827  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  1828  	}
  1829  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  1830  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  1831  	}
  1832  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  1833  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  1834  	}
  1835  	// credential change for server 2
  1836  	username2 = "test_user2_new"
  1837  	password2 = "test_password2_new"
  1838  	accessToken2 = "test/access/token/2/new"
  1839  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  1840  	if err != nil {
  1841  		t.Fatalf("failed to create test request: %v", err)
  1842  	}
  1843  	client2.Credential = StaticCredential(uri2.Host, Credential{
  1844  		Username: username2,
  1845  		Password: password2,
  1846  	})
  1847  	resp2, err = client2.Do(req2)
  1848  	if err != nil {
  1849  		t.Fatalf("Client.Do() error = %v", err)
  1850  	}
  1851  	if resp2.StatusCode != http.StatusOK {
  1852  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  1853  	}
  1854  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  1855  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  1856  	}
  1857  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  1858  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  1859  	}
  1860  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  1861  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  1862  	}
  1863  }
  1864  
  1865  func TestClient_Do_Bearer_OAuth2_RefreshToken(t *testing.T) {
  1866  	refreshToken := "test/refresh/token"
  1867  	accessToken := "test/access/token"
  1868  	var requestCount, wantRequestCount int64
  1869  	var successCount, wantSuccessCount int64
  1870  	var authCount, wantAuthCount int64
  1871  	var service string
  1872  	scopes := []string{
  1873  		"repository:dst:pull,push",
  1874  		"repository:src:pull",
  1875  	}
  1876  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1877  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1878  			t.Error("unexecuted attempt of authorization service")
  1879  			w.WriteHeader(http.StatusUnauthorized)
  1880  			return
  1881  		}
  1882  		if err := r.ParseForm(); err != nil {
  1883  			t.Errorf("failed to parse form: %v", err)
  1884  			w.WriteHeader(http.StatusUnauthorized)
  1885  			return
  1886  		}
  1887  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  1888  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  1889  			w.WriteHeader(http.StatusUnauthorized)
  1890  			return
  1891  		}
  1892  		if got := r.PostForm.Get("service"); got != service {
  1893  			t.Errorf("unexpected service: %v, want %v", got, service)
  1894  			w.WriteHeader(http.StatusUnauthorized)
  1895  			return
  1896  		}
  1897  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1898  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1899  			w.WriteHeader(http.StatusUnauthorized)
  1900  			return
  1901  		}
  1902  		scope := strings.Join(scopes, " ")
  1903  		if got := r.PostForm.Get("scope"); got != scope {
  1904  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1905  			w.WriteHeader(http.StatusUnauthorized)
  1906  			return
  1907  		}
  1908  		if got := r.PostForm.Get("refresh_token"); got != refreshToken {
  1909  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
  1910  			w.WriteHeader(http.StatusUnauthorized)
  1911  			return
  1912  		}
  1913  
  1914  		atomic.AddInt64(&authCount, 1)
  1915  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1916  			t.Errorf("failed to write %q: %v", r.URL, err)
  1917  		}
  1918  	}))
  1919  	defer as.Close()
  1920  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1921  		atomic.AddInt64(&requestCount, 1)
  1922  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1923  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1924  			w.WriteHeader(http.StatusNotFound)
  1925  			return
  1926  		}
  1927  		header := "Bearer " + accessToken
  1928  		if auth := r.Header.Get("Authorization"); auth != header {
  1929  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  1930  			w.Header().Set("Www-Authenticate", challenge)
  1931  			w.WriteHeader(http.StatusUnauthorized)
  1932  			return
  1933  		}
  1934  		atomic.AddInt64(&successCount, 1)
  1935  	}))
  1936  	defer ts.Close()
  1937  	uri, err := url.Parse(ts.URL)
  1938  	if err != nil {
  1939  		t.Fatalf("invalid test http server: %v", err)
  1940  	}
  1941  	service = uri.Host
  1942  
  1943  	client := &Client{
  1944  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1945  			if reg != uri.Host {
  1946  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1947  				t.Error(err)
  1948  				return EmptyCredential, err
  1949  			}
  1950  			return Credential{
  1951  				RefreshToken: refreshToken,
  1952  			}, nil
  1953  		},
  1954  	}
  1955  
  1956  	// first request
  1957  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  1958  	if err != nil {
  1959  		t.Fatalf("failed to create test request: %v", err)
  1960  	}
  1961  	resp, err := client.Do(req)
  1962  	if err != nil {
  1963  		t.Fatalf("Client.Do() error = %v", err)
  1964  	}
  1965  	if resp.StatusCode != http.StatusOK {
  1966  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1967  	}
  1968  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1969  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1970  	}
  1971  	if wantSuccessCount++; successCount != wantSuccessCount {
  1972  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1973  	}
  1974  	if wantAuthCount++; authCount != wantAuthCount {
  1975  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1976  	}
  1977  
  1978  	// credential change
  1979  	refreshToken = "test/refresh/token/2"
  1980  	accessToken = "test/access/token/2"
  1981  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
  1982  	if err != nil {
  1983  		t.Fatalf("failed to create test request: %v", err)
  1984  	}
  1985  	resp, err = client.Do(req)
  1986  	if err != nil {
  1987  		t.Fatalf("Client.Do() error = %v", err)
  1988  	}
  1989  	if resp.StatusCode != http.StatusOK {
  1990  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1991  	}
  1992  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1993  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1994  	}
  1995  	if wantSuccessCount++; successCount != wantSuccessCount {
  1996  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1997  	}
  1998  	if wantAuthCount++; authCount != wantAuthCount {
  1999  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  2000  	}
  2001  }
  2002  
  2003  func TestClient_Do_Bearer_OAuth2_RefreshToken_Cached(t *testing.T) {
  2004  	refreshToken := "test/refresh/token"
  2005  	accessToken := "test/access/token"
  2006  	var requestCount, wantRequestCount int64
  2007  	var successCount, wantSuccessCount int64
  2008  	var authCount, wantAuthCount int64
  2009  	var service string
  2010  	scopes := []string{
  2011  		"repository:dst:pull,push",
  2012  		"repository:src:pull",
  2013  	}
  2014  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2015  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  2016  			t.Error("unexecuted attempt of authorization service")
  2017  			w.WriteHeader(http.StatusUnauthorized)
  2018  			return
  2019  		}
  2020  		if err := r.ParseForm(); err != nil {
  2021  			t.Errorf("failed to parse form: %v", err)
  2022  			w.WriteHeader(http.StatusUnauthorized)
  2023  			return
  2024  		}
  2025  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  2026  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  2027  			w.WriteHeader(http.StatusUnauthorized)
  2028  			return
  2029  		}
  2030  		if got := r.PostForm.Get("service"); got != service {
  2031  			t.Errorf("unexpected service: %v, want %v", got, service)
  2032  			w.WriteHeader(http.StatusUnauthorized)
  2033  			return
  2034  		}
  2035  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  2036  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  2037  			w.WriteHeader(http.StatusUnauthorized)
  2038  			return
  2039  		}
  2040  		scope := strings.Join(scopes, " ")
  2041  		if got := r.PostForm.Get("scope"); got != scope {
  2042  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  2043  			w.WriteHeader(http.StatusUnauthorized)
  2044  			return
  2045  		}
  2046  		if got := r.PostForm.Get("refresh_token"); got != refreshToken {
  2047  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
  2048  			w.WriteHeader(http.StatusUnauthorized)
  2049  			return
  2050  		}
  2051  
  2052  		atomic.AddInt64(&authCount, 1)
  2053  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  2054  			t.Errorf("failed to write %q: %v", r.URL, err)
  2055  		}
  2056  	}))
  2057  	defer as.Close()
  2058  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2059  		atomic.AddInt64(&requestCount, 1)
  2060  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  2061  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  2062  			w.WriteHeader(http.StatusNotFound)
  2063  			return
  2064  		}
  2065  		header := "Bearer " + accessToken
  2066  		if auth := r.Header.Get("Authorization"); auth != header {
  2067  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  2068  			w.Header().Set("Www-Authenticate", challenge)
  2069  			w.WriteHeader(http.StatusUnauthorized)
  2070  			return
  2071  		}
  2072  		atomic.AddInt64(&successCount, 1)
  2073  	}))
  2074  	defer ts.Close()
  2075  	uri, err := url.Parse(ts.URL)
  2076  	if err != nil {
  2077  		t.Fatalf("invalid test http server: %v", err)
  2078  	}
  2079  	service = uri.Host
  2080  
  2081  	client := &Client{
  2082  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  2083  			if reg != uri.Host {
  2084  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  2085  				t.Error(err)
  2086  				return EmptyCredential, err
  2087  			}
  2088  			return Credential{
  2089  				RefreshToken: refreshToken,
  2090  			}, nil
  2091  		},
  2092  		Cache: NewCache(),
  2093  	}
  2094  
  2095  	// first request
  2096  	ctx := WithScopes(context.Background(), scopes...)
  2097  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  2098  	if err != nil {
  2099  		t.Fatalf("failed to create test request: %v", err)
  2100  	}
  2101  	resp, err := client.Do(req)
  2102  	if err != nil {
  2103  		t.Fatalf("Client.Do() error = %v", err)
  2104  	}
  2105  	if resp.StatusCode != http.StatusOK {
  2106  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  2107  	}
  2108  	if wantRequestCount += 2; requestCount != wantRequestCount {
  2109  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  2110  	}
  2111  	if wantSuccessCount++; successCount != wantSuccessCount {
  2112  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  2113  	}
  2114  	if wantAuthCount++; authCount != wantAuthCount {
  2115  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  2116  	}
  2117  
  2118  	// repeated request
  2119  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  2120  	if err != nil {
  2121  		t.Fatalf("failed to create test request: %v", err)
  2122  	}
  2123  	resp, err = client.Do(req)
  2124  	if err != nil {
  2125  		t.Fatalf("Client.Do() error = %v", err)
  2126  	}
  2127  	if resp.StatusCode != http.StatusOK {
  2128  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  2129  	}
  2130  	if wantRequestCount++; requestCount != wantRequestCount {
  2131  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  2132  	}
  2133  	if wantSuccessCount++; successCount != wantSuccessCount {
  2134  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  2135  	}
  2136  	if authCount != wantAuthCount {
  2137  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  2138  	}
  2139  
  2140  	// credential change
  2141  	refreshToken = "test/refresh/token/2"
  2142  	accessToken = "test/access/token/2"
  2143  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  2144  	if err != nil {
  2145  		t.Fatalf("failed to create test request: %v", err)
  2146  	}
  2147  	resp, err = client.Do(req)
  2148  	if err != nil {
  2149  		t.Fatalf("Client.Do() error = %v", err)
  2150  	}
  2151  	if resp.StatusCode != http.StatusOK {
  2152  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  2153  	}
  2154  	if wantRequestCount += 2; requestCount != wantRequestCount {
  2155  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  2156  	}
  2157  	if wantSuccessCount++; successCount != wantSuccessCount {
  2158  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  2159  	}
  2160  	if wantAuthCount++; authCount != wantAuthCount {
  2161  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  2162  	}
  2163  }
  2164  
  2165  func TestClient_Do_Bearer_OAuth2_RefreshToken_Cached_PerHost(t *testing.T) {
  2166  	// set up server 1
  2167  	refreshToken1 := "test/refresh/token/1"
  2168  	accessToken1 := "test/access/token/1"
  2169  	var requestCount1, wantRequestCount1 int64
  2170  	var successCount1, wantSuccessCount1 int64
  2171  	var authCount1, wantAuthCount1 int64
  2172  	var service1 string
  2173  	scopes1 := []string{
  2174  		"repository:src:pull",
  2175  	}
  2176  	as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2177  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  2178  			t.Error("unexecuted attempt of authorization service")
  2179  			w.WriteHeader(http.StatusUnauthorized)
  2180  			return
  2181  		}
  2182  		if err := r.ParseForm(); err != nil {
  2183  			t.Errorf("failed to parse form: %v", err)
  2184  			w.WriteHeader(http.StatusUnauthorized)
  2185  			return
  2186  		}
  2187  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  2188  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  2189  			w.WriteHeader(http.StatusUnauthorized)
  2190  			return
  2191  		}
  2192  		if got := r.PostForm.Get("service"); got != service1 {
  2193  			t.Errorf("unexpected service: %v, want %v", got, service1)
  2194  			w.WriteHeader(http.StatusUnauthorized)
  2195  			return
  2196  		}
  2197  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  2198  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  2199  			w.WriteHeader(http.StatusUnauthorized)
  2200  			return
  2201  		}
  2202  		scope := strings.Join(scopes1, " ")
  2203  		if got := r.PostForm.Get("scope"); got != scope {
  2204  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  2205  			w.WriteHeader(http.StatusUnauthorized)
  2206  			return
  2207  		}
  2208  		if got := r.PostForm.Get("refresh_token"); got != refreshToken1 {
  2209  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken1)
  2210  			w.WriteHeader(http.StatusUnauthorized)
  2211  			return
  2212  		}
  2213  
  2214  		atomic.AddInt64(&authCount1, 1)
  2215  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil {
  2216  			t.Errorf("failed to write %q: %v", r.URL, err)
  2217  		}
  2218  	}))
  2219  	defer as1.Close()
  2220  	ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2221  		atomic.AddInt64(&requestCount1, 1)
  2222  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  2223  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  2224  			w.WriteHeader(http.StatusNotFound)
  2225  			return
  2226  		}
  2227  		header := "Bearer " + accessToken1
  2228  		if auth := r.Header.Get("Authorization"); auth != header {
  2229  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, strings.Join(scopes1, " "))
  2230  			w.Header().Set("Www-Authenticate", challenge)
  2231  			w.WriteHeader(http.StatusUnauthorized)
  2232  			return
  2233  		}
  2234  		atomic.AddInt64(&successCount1, 1)
  2235  	}))
  2236  	defer ts1.Close()
  2237  	uri1, err := url.Parse(ts1.URL)
  2238  	if err != nil {
  2239  		t.Fatalf("invalid test http server: %v", err)
  2240  	}
  2241  	service1 = uri1.Host
  2242  	client1 := &Client{
  2243  		Credential: StaticCredential(uri1.Host, Credential{
  2244  			RefreshToken: refreshToken1,
  2245  		}),
  2246  		Cache: NewCache(),
  2247  	}
  2248  
  2249  	// set up server 2
  2250  	refreshToken2 := "test/refresh/token/1"
  2251  	accessToken2 := "test/access/token/1"
  2252  	var requestCount2, wantRequestCount2 int64
  2253  	var successCount2, wantSuccessCount2 int64
  2254  	var authCount2, wantAuthCount2 int64
  2255  	var service2 string
  2256  	scopes2 := []string{
  2257  		"repository:dst:pull,push",
  2258  	}
  2259  	as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2260  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  2261  			t.Error("unexecuted attempt of authorization service")
  2262  			w.WriteHeader(http.StatusUnauthorized)
  2263  			return
  2264  		}
  2265  		if err := r.ParseForm(); err != nil {
  2266  			t.Errorf("failed to parse form: %v", err)
  2267  			w.WriteHeader(http.StatusUnauthorized)
  2268  			return
  2269  		}
  2270  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  2271  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  2272  			w.WriteHeader(http.StatusUnauthorized)
  2273  			return
  2274  		}
  2275  		if got := r.PostForm.Get("service"); got != service2 {
  2276  			t.Errorf("unexpected service: %v, want %v", got, service2)
  2277  			w.WriteHeader(http.StatusUnauthorized)
  2278  			return
  2279  		}
  2280  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  2281  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  2282  			w.WriteHeader(http.StatusUnauthorized)
  2283  			return
  2284  		}
  2285  		scope := strings.Join(scopes2, " ")
  2286  		if got := r.PostForm.Get("scope"); got != scope {
  2287  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  2288  			w.WriteHeader(http.StatusUnauthorized)
  2289  			return
  2290  		}
  2291  		if got := r.PostForm.Get("refresh_token"); got != refreshToken2 {
  2292  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken2)
  2293  			w.WriteHeader(http.StatusUnauthorized)
  2294  			return
  2295  		}
  2296  
  2297  		atomic.AddInt64(&authCount2, 1)
  2298  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil {
  2299  			t.Errorf("failed to write %q: %v", r.URL, err)
  2300  		}
  2301  	}))
  2302  	defer as2.Close()
  2303  	ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2304  		atomic.AddInt64(&requestCount2, 1)
  2305  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  2306  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  2307  			w.WriteHeader(http.StatusNotFound)
  2308  			return
  2309  		}
  2310  		header := "Bearer " + accessToken2
  2311  		if auth := r.Header.Get("Authorization"); auth != header {
  2312  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, strings.Join(scopes2, " "))
  2313  			w.Header().Set("Www-Authenticate", challenge)
  2314  			w.WriteHeader(http.StatusUnauthorized)
  2315  			return
  2316  		}
  2317  		atomic.AddInt64(&successCount2, 1)
  2318  	}))
  2319  	defer ts2.Close()
  2320  	uri2, err := url.Parse(ts2.URL)
  2321  	if err != nil {
  2322  		t.Fatalf("invalid test http server: %v", err)
  2323  	}
  2324  	service2 = uri2.Host
  2325  	client2 := &Client{
  2326  		Credential: StaticCredential(uri2.Host, Credential{
  2327  			RefreshToken: refreshToken2,
  2328  		}),
  2329  		Cache: NewCache(),
  2330  	}
  2331  
  2332  	ctx := context.Background()
  2333  	ctx = WithScopesForHost(ctx, uri1.Host, scopes1...)
  2334  	ctx = WithScopesForHost(ctx, uri2.Host, scopes2...)
  2335  	// first request to server 1
  2336  	req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  2337  	if err != nil {
  2338  		t.Fatalf("failed to create test request: %v", err)
  2339  	}
  2340  	resp1, err := client1.Do(req1)
  2341  	if err != nil {
  2342  		t.Fatalf("Client.Do() error = %v", err)
  2343  	}
  2344  	if resp1.StatusCode != http.StatusOK {
  2345  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  2346  	}
  2347  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  2348  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  2349  	}
  2350  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  2351  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  2352  	}
  2353  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  2354  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  2355  	}
  2356  
  2357  	// first request to server 2
  2358  	req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  2359  	if err != nil {
  2360  		t.Fatalf("failed to create test request: %v", err)
  2361  	}
  2362  	resp2, err := client2.Do(req2)
  2363  	if err != nil {
  2364  		t.Fatalf("Client.Do() error = %v", err)
  2365  	}
  2366  	if resp2.StatusCode != http.StatusOK {
  2367  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  2368  	}
  2369  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  2370  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  2371  	}
  2372  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  2373  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  2374  	}
  2375  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  2376  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  2377  	}
  2378  
  2379  	// repeated request to server 1
  2380  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  2381  	if err != nil {
  2382  		t.Fatalf("failed to create test request: %v", err)
  2383  	}
  2384  	resp1, err = client1.Do(req1)
  2385  	if err != nil {
  2386  		t.Fatalf("Client.Do() error = %v", err)
  2387  	}
  2388  	if resp1.StatusCode != http.StatusOK {
  2389  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  2390  	}
  2391  	if wantRequestCount1++; requestCount1 != wantRequestCount1 {
  2392  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  2393  	}
  2394  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  2395  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  2396  	}
  2397  	if authCount1 != wantAuthCount1 {
  2398  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  2399  	}
  2400  	// repeated request to server 2
  2401  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  2402  	if err != nil {
  2403  		t.Fatalf("failed to create test request: %v", err)
  2404  	}
  2405  	resp2, err = client2.Do(req2)
  2406  	if err != nil {
  2407  		t.Fatalf("Client.Do() error = %v", err)
  2408  	}
  2409  	if resp2.StatusCode != http.StatusOK {
  2410  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  2411  	}
  2412  	if wantRequestCount2++; requestCount2 != wantRequestCount2 {
  2413  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  2414  	}
  2415  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  2416  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  2417  	}
  2418  	if authCount2 != wantAuthCount2 {
  2419  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  2420  	}
  2421  
  2422  	// credential change to server 1
  2423  	refreshToken1 = "test/refresh/token/1/new"
  2424  	accessToken1 = "test/access/token/1/new"
  2425  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  2426  	if err != nil {
  2427  		t.Fatalf("failed to create test request: %v", err)
  2428  	}
  2429  	client1.Credential = StaticCredential(uri1.Host, Credential{
  2430  		RefreshToken: refreshToken1,
  2431  	})
  2432  	resp1, err = client1.Do(req1)
  2433  	if err != nil {
  2434  		t.Fatalf("Client.Do() error = %v", err)
  2435  	}
  2436  	if resp1.StatusCode != http.StatusOK {
  2437  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  2438  	}
  2439  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  2440  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  2441  	}
  2442  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  2443  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  2444  	}
  2445  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  2446  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  2447  	}
  2448  	// credential change to server 2
  2449  	refreshToken2 = "test/refresh/token/2/new"
  2450  	accessToken2 = "test/access/token/2/new"
  2451  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  2452  	if err != nil {
  2453  		t.Fatalf("failed to create test request: %v", err)
  2454  	}
  2455  	client2.Credential = StaticCredential(uri2.Host, Credential{
  2456  		RefreshToken: refreshToken2,
  2457  	})
  2458  	resp2, err = client2.Do(req2)
  2459  	if err != nil {
  2460  		t.Fatalf("Client.Do() error = %v", err)
  2461  	}
  2462  	if resp2.StatusCode != http.StatusOK {
  2463  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  2464  	}
  2465  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  2466  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  2467  	}
  2468  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  2469  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  2470  	}
  2471  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  2472  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  2473  	}
  2474  }
  2475  
  2476  func TestClient_Do_Token_Expire(t *testing.T) {
  2477  	refreshToken := "test/refresh/token"
  2478  	accessToken := "test/access/token"
  2479  	var requestCount, wantRequestCount int64
  2480  	var successCount, wantSuccessCount int64
  2481  	var authCount, wantAuthCount int64
  2482  	var service string
  2483  	scopes := []string{
  2484  		"repository:dst:pull,push",
  2485  		"repository:src:pull",
  2486  	}
  2487  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2488  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  2489  			t.Error("unexecuted attempt of authorization service")
  2490  			w.WriteHeader(http.StatusUnauthorized)
  2491  			return
  2492  		}
  2493  		if err := r.ParseForm(); err != nil {
  2494  			t.Errorf("failed to parse form: %v", err)
  2495  			w.WriteHeader(http.StatusUnauthorized)
  2496  			return
  2497  		}
  2498  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  2499  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  2500  			w.WriteHeader(http.StatusUnauthorized)
  2501  			return
  2502  		}
  2503  		if got := r.PostForm.Get("service"); got != service {
  2504  			t.Errorf("unexpected service: %v, want %v", got, service)
  2505  			w.WriteHeader(http.StatusUnauthorized)
  2506  			return
  2507  		}
  2508  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  2509  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  2510  			w.WriteHeader(http.StatusUnauthorized)
  2511  			return
  2512  		}
  2513  		scope := strings.Join(scopes, " ")
  2514  		if got := r.PostForm.Get("scope"); got != scope {
  2515  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  2516  			w.WriteHeader(http.StatusUnauthorized)
  2517  			return
  2518  		}
  2519  		if got := r.PostForm.Get("refresh_token"); got != refreshToken {
  2520  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
  2521  			w.WriteHeader(http.StatusUnauthorized)
  2522  			return
  2523  		}
  2524  
  2525  		atomic.AddInt64(&authCount, 1)
  2526  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  2527  			t.Errorf("failed to write %q: %v", r.URL, err)
  2528  		}
  2529  	}))
  2530  	defer as.Close()
  2531  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2532  		atomic.AddInt64(&requestCount, 1)
  2533  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  2534  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  2535  			w.WriteHeader(http.StatusNotFound)
  2536  			return
  2537  		}
  2538  		header := "Bearer " + accessToken
  2539  		if auth := r.Header.Get("Authorization"); auth != header {
  2540  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  2541  			w.Header().Set("Www-Authenticate", challenge)
  2542  			w.WriteHeader(http.StatusUnauthorized)
  2543  			return
  2544  		}
  2545  		atomic.AddInt64(&successCount, 1)
  2546  	}))
  2547  	defer ts.Close()
  2548  	uri, err := url.Parse(ts.URL)
  2549  	if err != nil {
  2550  		t.Fatalf("invalid test http server: %v", err)
  2551  	}
  2552  	service = uri.Host
  2553  
  2554  	client := &Client{
  2555  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  2556  			if reg != uri.Host {
  2557  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  2558  				t.Error(err)
  2559  				return EmptyCredential, err
  2560  			}
  2561  			return Credential{
  2562  				RefreshToken: refreshToken,
  2563  			}, nil
  2564  		},
  2565  		Cache: NewCache(),
  2566  	}
  2567  
  2568  	// first request
  2569  	ctx := WithScopes(context.Background(), scopes...)
  2570  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  2571  	if err != nil {
  2572  		t.Fatalf("failed to create test request: %v", err)
  2573  	}
  2574  	resp, err := client.Do(req)
  2575  	if err != nil {
  2576  		t.Fatalf("Client.Do() error = %v", err)
  2577  	}
  2578  	if resp.StatusCode != http.StatusOK {
  2579  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  2580  	}
  2581  	if wantRequestCount += 2; requestCount != wantRequestCount {
  2582  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  2583  	}
  2584  	if wantSuccessCount++; successCount != wantSuccessCount {
  2585  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  2586  	}
  2587  	if wantAuthCount++; authCount != wantAuthCount {
  2588  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  2589  	}
  2590  
  2591  	// invalidate the access token and request again
  2592  	accessToken = "test/access/token/2"
  2593  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  2594  	if err != nil {
  2595  		t.Fatalf("failed to create test request: %v", err)
  2596  	}
  2597  	resp, err = client.Do(req)
  2598  	if err != nil {
  2599  		t.Fatalf("Client.Do() error = %v", err)
  2600  	}
  2601  	if resp.StatusCode != http.StatusOK {
  2602  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  2603  	}
  2604  	if wantRequestCount += 2; requestCount != wantRequestCount {
  2605  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  2606  	}
  2607  	if wantSuccessCount++; successCount != wantSuccessCount {
  2608  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  2609  	}
  2610  	if wantAuthCount++; authCount != wantAuthCount {
  2611  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  2612  	}
  2613  }
  2614  
  2615  func TestClient_Do_Token_Expire_PerHost(t *testing.T) {
  2616  	// set up server 1
  2617  	refreshToken1 := "test/refresh/token/1"
  2618  	accessToken1 := "test/access/token/1"
  2619  	var requestCount1, wantRequestCount1 int64
  2620  	var successCount1, wantSuccessCount1 int64
  2621  	var authCount1, wantAuthCount1 int64
  2622  	var service1 string
  2623  	scopes1 := []string{
  2624  		"repository:src:pull",
  2625  	}
  2626  	as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2627  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  2628  			t.Error("unexecuted attempt of authorization service")
  2629  			w.WriteHeader(http.StatusUnauthorized)
  2630  			return
  2631  		}
  2632  		if err := r.ParseForm(); err != nil {
  2633  			t.Errorf("failed to parse form: %v", err)
  2634  			w.WriteHeader(http.StatusUnauthorized)
  2635  			return
  2636  		}
  2637  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  2638  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  2639  			w.WriteHeader(http.StatusUnauthorized)
  2640  			return
  2641  		}
  2642  		if got := r.PostForm.Get("service"); got != service1 {
  2643  			t.Errorf("unexpected service: %v, want %v", got, service1)
  2644  			w.WriteHeader(http.StatusUnauthorized)
  2645  			return
  2646  		}
  2647  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  2648  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  2649  			w.WriteHeader(http.StatusUnauthorized)
  2650  			return
  2651  		}
  2652  		scope := strings.Join(scopes1, " ")
  2653  		if got := r.PostForm.Get("scope"); got != scope {
  2654  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  2655  			w.WriteHeader(http.StatusUnauthorized)
  2656  			return
  2657  		}
  2658  		if got := r.PostForm.Get("refresh_token"); got != refreshToken1 {
  2659  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken1)
  2660  			w.WriteHeader(http.StatusUnauthorized)
  2661  			return
  2662  		}
  2663  
  2664  		atomic.AddInt64(&authCount1, 1)
  2665  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil {
  2666  			t.Errorf("failed to write %q: %v", r.URL, err)
  2667  		}
  2668  	}))
  2669  	defer as1.Close()
  2670  	ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2671  		atomic.AddInt64(&requestCount1, 1)
  2672  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  2673  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  2674  			w.WriteHeader(http.StatusNotFound)
  2675  			return
  2676  		}
  2677  		header := "Bearer " + accessToken1
  2678  		if auth := r.Header.Get("Authorization"); auth != header {
  2679  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, strings.Join(scopes1, " "))
  2680  			w.Header().Set("Www-Authenticate", challenge)
  2681  			w.WriteHeader(http.StatusUnauthorized)
  2682  			return
  2683  		}
  2684  		atomic.AddInt64(&successCount1, 1)
  2685  	}))
  2686  	defer ts1.Close()
  2687  	uri1, err := url.Parse(ts1.URL)
  2688  	if err != nil {
  2689  		t.Fatalf("invalid test http server: %v", err)
  2690  	}
  2691  	service1 = uri1.Host
  2692  	client1 := &Client{
  2693  		Credential: StaticCredential(uri1.Host, Credential{
  2694  			RefreshToken: refreshToken1,
  2695  		}),
  2696  		Cache: NewCache(),
  2697  	}
  2698  	// set up server 2
  2699  	refreshToken2 := "test/refresh/token/2"
  2700  	accessToken2 := "test/access/token/2"
  2701  	var requestCount2, wantRequestCount2 int64
  2702  	var successCount2, wantSuccessCount2 int64
  2703  	var authCount2, wantAuthCount2 int64
  2704  	var service2 string
  2705  	scopes2 := []string{
  2706  		"repository:dst:pull,push",
  2707  	}
  2708  	as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2709  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  2710  			t.Error("unexecuted attempt of authorization service")
  2711  			w.WriteHeader(http.StatusUnauthorized)
  2712  			return
  2713  		}
  2714  		if err := r.ParseForm(); err != nil {
  2715  			t.Errorf("failed to parse form: %v", err)
  2716  			w.WriteHeader(http.StatusUnauthorized)
  2717  			return
  2718  		}
  2719  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  2720  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  2721  			w.WriteHeader(http.StatusUnauthorized)
  2722  			return
  2723  		}
  2724  		if got := r.PostForm.Get("service"); got != service2 {
  2725  			t.Errorf("unexpected service: %v, want %v", got, service2)
  2726  			w.WriteHeader(http.StatusUnauthorized)
  2727  			return
  2728  		}
  2729  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  2730  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  2731  			w.WriteHeader(http.StatusUnauthorized)
  2732  			return
  2733  		}
  2734  		scope := strings.Join(scopes2, " ")
  2735  		if got := r.PostForm.Get("scope"); got != scope {
  2736  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  2737  			w.WriteHeader(http.StatusUnauthorized)
  2738  			return
  2739  		}
  2740  		if got := r.PostForm.Get("refresh_token"); got != refreshToken2 {
  2741  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken2)
  2742  			w.WriteHeader(http.StatusUnauthorized)
  2743  			return
  2744  		}
  2745  
  2746  		atomic.AddInt64(&authCount2, 1)
  2747  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil {
  2748  			t.Errorf("failed to write %q: %v", r.URL, err)
  2749  		}
  2750  	}))
  2751  	defer as2.Close()
  2752  	ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2753  		atomic.AddInt64(&requestCount2, 1)
  2754  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  2755  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  2756  			w.WriteHeader(http.StatusNotFound)
  2757  			return
  2758  		}
  2759  		header := "Bearer " + accessToken2
  2760  		if auth := r.Header.Get("Authorization"); auth != header {
  2761  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, strings.Join(scopes2, " "))
  2762  			w.Header().Set("Www-Authenticate", challenge)
  2763  			w.WriteHeader(http.StatusUnauthorized)
  2764  			return
  2765  		}
  2766  		atomic.AddInt64(&successCount2, 1)
  2767  	}))
  2768  	defer ts2.Close()
  2769  	uri2, err := url.Parse(ts2.URL)
  2770  	if err != nil {
  2771  		t.Fatalf("invalid test http server: %v", err)
  2772  	}
  2773  	service2 = uri2.Host
  2774  	client2 := &Client{
  2775  		Credential: StaticCredential(uri2.Host, Credential{
  2776  			RefreshToken: refreshToken2,
  2777  		}),
  2778  		Cache: NewCache(),
  2779  	}
  2780  
  2781  	ctx := context.Background()
  2782  	ctx = WithScopesForHost(ctx, uri1.Host, scopes1...)
  2783  	ctx = WithScopesForHost(ctx, uri2.Host, scopes2...)
  2784  	// first request to server 1
  2785  	req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  2786  	if err != nil {
  2787  		t.Fatalf("failed to create test request: %v", err)
  2788  	}
  2789  	resp1, err := client1.Do(req1)
  2790  	if err != nil {
  2791  		t.Fatalf("Client.Do() error = %v", err)
  2792  	}
  2793  	if resp1.StatusCode != http.StatusOK {
  2794  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  2795  	}
  2796  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  2797  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  2798  	}
  2799  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  2800  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  2801  	}
  2802  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  2803  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  2804  	}
  2805  
  2806  	// first request to server 2
  2807  	req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  2808  	if err != nil {
  2809  		t.Fatalf("failed to create test request: %v", err)
  2810  	}
  2811  	resp2, err := client2.Do(req2)
  2812  	if err != nil {
  2813  		t.Fatalf("Client.Do() error = %v", err)
  2814  	}
  2815  	if resp2.StatusCode != http.StatusOK {
  2816  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  2817  	}
  2818  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  2819  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  2820  	}
  2821  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  2822  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  2823  	}
  2824  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  2825  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  2826  	}
  2827  
  2828  	// invalidate the access token and request again to server 1
  2829  	accessToken1 = "test/access/token/1/new"
  2830  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  2831  	if err != nil {
  2832  		t.Fatalf("failed to create test request: %v", err)
  2833  	}
  2834  	resp1, err = client1.Do(req1)
  2835  	if err != nil {
  2836  		t.Fatalf("Client.Do() error = %v", err)
  2837  	}
  2838  	if resp1.StatusCode != http.StatusOK {
  2839  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  2840  	}
  2841  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  2842  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  2843  	}
  2844  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  2845  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  2846  	}
  2847  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  2848  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  2849  	}
  2850  	// invalidate the access token and request again to server 2
  2851  	accessToken2 = "test/access/token/2/new"
  2852  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  2853  	if err != nil {
  2854  		t.Fatalf("failed to create test request: %v", err)
  2855  	}
  2856  	resp2, err = client2.Do(req2)
  2857  	if err != nil {
  2858  		t.Fatalf("Client.Do() error = %v", err)
  2859  	}
  2860  	if resp2.StatusCode != http.StatusOK {
  2861  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  2862  	}
  2863  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  2864  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  2865  	}
  2866  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  2867  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  2868  	}
  2869  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  2870  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  2871  	}
  2872  }
  2873  
  2874  func TestClient_Do_Scope_Hint_Mismatch(t *testing.T) {
  2875  	username := "test_user"
  2876  	password := "test_password"
  2877  	accessToken := "test/access/token"
  2878  	var requestCount, wantRequestCount int64
  2879  	var successCount, wantSuccessCount int64
  2880  	var authCount, wantAuthCount int64
  2881  	var service string
  2882  	scopes := []string{
  2883  		"repository:dst:pull,push",
  2884  		"repository:src:pull",
  2885  	}
  2886  	scope := "repository:test:delete"
  2887  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2888  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  2889  			t.Error("unexecuted attempt of authorization service")
  2890  			w.WriteHeader(http.StatusUnauthorized)
  2891  			return
  2892  		}
  2893  		if err := r.ParseForm(); err != nil {
  2894  			t.Errorf("failed to parse form: %v", err)
  2895  			w.WriteHeader(http.StatusUnauthorized)
  2896  			return
  2897  		}
  2898  		if got := r.PostForm.Get("grant_type"); got != "password" {
  2899  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
  2900  			w.WriteHeader(http.StatusUnauthorized)
  2901  			return
  2902  		}
  2903  		if got := r.PostForm.Get("service"); got != service {
  2904  			t.Errorf("unexpected service: %v, want %v", got, service)
  2905  			w.WriteHeader(http.StatusUnauthorized)
  2906  			return
  2907  		}
  2908  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  2909  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  2910  			w.WriteHeader(http.StatusUnauthorized)
  2911  			return
  2912  		}
  2913  		scopes := CleanScopes(append([]string{scope}, scopes...))
  2914  		scope := strings.Join(scopes, " ")
  2915  		if got := r.PostForm.Get("scope"); got != scope {
  2916  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  2917  			w.WriteHeader(http.StatusUnauthorized)
  2918  			return
  2919  		}
  2920  		if got := r.PostForm.Get("username"); got != username {
  2921  			t.Errorf("unexpected username: %v, want %v", got, username)
  2922  			w.WriteHeader(http.StatusUnauthorized)
  2923  			return
  2924  		}
  2925  		if got := r.PostForm.Get("password"); got != password {
  2926  			t.Errorf("unexpected password: %v, want %v", got, password)
  2927  			w.WriteHeader(http.StatusUnauthorized)
  2928  			return
  2929  		}
  2930  
  2931  		atomic.AddInt64(&authCount, 1)
  2932  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  2933  			t.Errorf("failed to write %q: %v", r.URL, err)
  2934  		}
  2935  	}))
  2936  	defer as.Close()
  2937  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2938  		atomic.AddInt64(&requestCount, 1)
  2939  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  2940  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  2941  			w.WriteHeader(http.StatusNotFound)
  2942  			return
  2943  		}
  2944  		header := "Bearer " + accessToken
  2945  		if auth := r.Header.Get("Authorization"); auth != header {
  2946  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
  2947  			w.Header().Set("Www-Authenticate", challenge)
  2948  			w.WriteHeader(http.StatusUnauthorized)
  2949  			return
  2950  		}
  2951  		atomic.AddInt64(&successCount, 1)
  2952  	}))
  2953  	defer ts.Close()
  2954  	uri, err := url.Parse(ts.URL)
  2955  	if err != nil {
  2956  		t.Fatalf("invalid test http server: %v", err)
  2957  	}
  2958  	service = uri.Host
  2959  
  2960  	client := &Client{
  2961  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  2962  			if reg != uri.Host {
  2963  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  2964  				t.Error(err)
  2965  				return EmptyCredential, err
  2966  			}
  2967  			return Credential{
  2968  				Username: username,
  2969  				Password: password,
  2970  			}, nil
  2971  		},
  2972  		ForceAttemptOAuth2: true,
  2973  		Cache:              NewCache(),
  2974  	}
  2975  
  2976  	// first request
  2977  	ctx := WithScopes(context.Background(), scopes...)
  2978  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  2979  	if err != nil {
  2980  		t.Fatalf("failed to create test request: %v", err)
  2981  	}
  2982  	resp, err := client.Do(req)
  2983  	if err != nil {
  2984  		t.Fatalf("Client.Do() error = %v", err)
  2985  	}
  2986  	if resp.StatusCode != http.StatusOK {
  2987  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  2988  	}
  2989  	if wantRequestCount += 2; requestCount != wantRequestCount {
  2990  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  2991  	}
  2992  	if wantSuccessCount++; successCount != wantSuccessCount {
  2993  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  2994  	}
  2995  	if wantAuthCount++; authCount != wantAuthCount {
  2996  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  2997  	}
  2998  
  2999  	// repeated request
  3000  	// although the actual scope does not match the hinted scopes, the client
  3001  	// with cache cannot avoid a request to obtain a challenge but can prevent
  3002  	// a repeated call to the authorization server.
  3003  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  3004  	if err != nil {
  3005  		t.Fatalf("failed to create test request: %v", err)
  3006  	}
  3007  	resp, err = client.Do(req)
  3008  	if err != nil {
  3009  		t.Fatalf("Client.Do() error = %v", err)
  3010  	}
  3011  	if resp.StatusCode != http.StatusOK {
  3012  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  3013  	}
  3014  	if wantRequestCount += 2; requestCount != wantRequestCount {
  3015  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  3016  	}
  3017  	if wantSuccessCount++; successCount != wantSuccessCount {
  3018  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  3019  	}
  3020  	if authCount != wantAuthCount {
  3021  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  3022  	}
  3023  }
  3024  
  3025  func TestClient_Do_Scope_Hint_Mismatch_PerHost(t *testing.T) {
  3026  	// set up server 1
  3027  	username1 := "test_user1"
  3028  	password1 := "test_password1"
  3029  	accessToken1 := "test/access/token/1"
  3030  	var requestCount1, wantRequestCount1 int64
  3031  	var successCount1, wantSuccessCount1 int64
  3032  	var authCount1, wantAuthCount1 int64
  3033  	var service1 string
  3034  	scopes1 := []string{
  3035  		"repository:dst:pull,push",
  3036  		"repository:src:pull",
  3037  	}
  3038  	scope1 := "repository:test1:delete"
  3039  	as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3040  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  3041  			t.Error("unexecuted attempt of authorization service")
  3042  			w.WriteHeader(http.StatusUnauthorized)
  3043  			return
  3044  		}
  3045  		if err := r.ParseForm(); err != nil {
  3046  			t.Errorf("failed to parse form: %v", err)
  3047  			w.WriteHeader(http.StatusUnauthorized)
  3048  			return
  3049  		}
  3050  		if got := r.PostForm.Get("grant_type"); got != "password" {
  3051  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
  3052  			w.WriteHeader(http.StatusUnauthorized)
  3053  			return
  3054  		}
  3055  		if got := r.PostForm.Get("service"); got != service1 {
  3056  			t.Errorf("unexpected service: %v, want %v", got, service1)
  3057  			w.WriteHeader(http.StatusUnauthorized)
  3058  			return
  3059  		}
  3060  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  3061  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  3062  			w.WriteHeader(http.StatusUnauthorized)
  3063  			return
  3064  		}
  3065  		scopes := CleanScopes(append([]string{scope1}, scopes1...))
  3066  		scope := strings.Join(scopes, " ")
  3067  		if got := r.PostForm.Get("scope"); got != scope {
  3068  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  3069  			w.WriteHeader(http.StatusUnauthorized)
  3070  			return
  3071  		}
  3072  		if got := r.PostForm.Get("username"); got != username1 {
  3073  			t.Errorf("unexpected username: %v, want %v", got, username1)
  3074  			w.WriteHeader(http.StatusUnauthorized)
  3075  			return
  3076  		}
  3077  		if got := r.PostForm.Get("password"); got != password1 {
  3078  			t.Errorf("unexpected password: %v, want %v", got, password1)
  3079  			w.WriteHeader(http.StatusUnauthorized)
  3080  			return
  3081  		}
  3082  
  3083  		atomic.AddInt64(&authCount1, 1)
  3084  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil {
  3085  			t.Errorf("failed to write %q: %v", r.URL, err)
  3086  		}
  3087  	}))
  3088  	defer as1.Close()
  3089  	ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3090  		atomic.AddInt64(&requestCount1, 1)
  3091  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3092  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  3093  			w.WriteHeader(http.StatusNotFound)
  3094  			return
  3095  		}
  3096  		header := "Bearer " + accessToken1
  3097  		if auth := r.Header.Get("Authorization"); auth != header {
  3098  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, scope1)
  3099  			w.Header().Set("Www-Authenticate", challenge)
  3100  			w.WriteHeader(http.StatusUnauthorized)
  3101  			return
  3102  		}
  3103  		atomic.AddInt64(&successCount1, 1)
  3104  	}))
  3105  	defer ts1.Close()
  3106  	uri1, err := url.Parse(ts1.URL)
  3107  	if err != nil {
  3108  		t.Fatalf("invalid test http server: %v", err)
  3109  	}
  3110  	service1 = uri1.Host
  3111  	client1 := &Client{
  3112  		Credential: StaticCredential(uri1.Host, Credential{
  3113  			Username: username1,
  3114  			Password: password1,
  3115  		}),
  3116  		ForceAttemptOAuth2: true,
  3117  		Cache:              NewCache(),
  3118  	}
  3119  
  3120  	// set up server 1
  3121  	username2 := "test_user2"
  3122  	password2 := "test_password2"
  3123  	accessToken2 := "test/access/token/2"
  3124  	var requestCount2, wantRequestCount2 int64
  3125  	var successCount2, wantSuccessCount2 int64
  3126  	var authCount2, wantAuthCount2 int64
  3127  	var service2 string
  3128  	scopes2 := []string{
  3129  		"repository:dst:pull,push",
  3130  		"repository:src:pull",
  3131  	}
  3132  	scope2 := "repository:test2:delete"
  3133  	as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3134  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  3135  			t.Error("unexecuted attempt of authorization service")
  3136  			w.WriteHeader(http.StatusUnauthorized)
  3137  			return
  3138  		}
  3139  		if err := r.ParseForm(); err != nil {
  3140  			t.Errorf("failed to parse form: %v", err)
  3141  			w.WriteHeader(http.StatusUnauthorized)
  3142  			return
  3143  		}
  3144  		if got := r.PostForm.Get("grant_type"); got != "password" {
  3145  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
  3146  			w.WriteHeader(http.StatusUnauthorized)
  3147  			return
  3148  		}
  3149  		if got := r.PostForm.Get("service"); got != service2 {
  3150  			t.Errorf("unexpected service: %v, want %v", got, service2)
  3151  			w.WriteHeader(http.StatusUnauthorized)
  3152  			return
  3153  		}
  3154  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  3155  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  3156  			w.WriteHeader(http.StatusUnauthorized)
  3157  			return
  3158  		}
  3159  		scopes := CleanScopes(append([]string{scope2}, scopes2...))
  3160  		scope := strings.Join(scopes, " ")
  3161  		if got := r.PostForm.Get("scope"); got != scope {
  3162  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  3163  			w.WriteHeader(http.StatusUnauthorized)
  3164  			return
  3165  		}
  3166  		if got := r.PostForm.Get("username"); got != username2 {
  3167  			t.Errorf("unexpected username: %v, want %v", got, username2)
  3168  			w.WriteHeader(http.StatusUnauthorized)
  3169  			return
  3170  		}
  3171  		if got := r.PostForm.Get("password"); got != password2 {
  3172  			t.Errorf("unexpected password: %v, want %v", got, password2)
  3173  			w.WriteHeader(http.StatusUnauthorized)
  3174  			return
  3175  		}
  3176  
  3177  		atomic.AddInt64(&authCount2, 1)
  3178  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil {
  3179  			t.Errorf("failed to write %q: %v", r.URL, err)
  3180  		}
  3181  	}))
  3182  	defer as2.Close()
  3183  	ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3184  		atomic.AddInt64(&requestCount2, 1)
  3185  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3186  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  3187  			w.WriteHeader(http.StatusNotFound)
  3188  			return
  3189  		}
  3190  		header := "Bearer " + accessToken2
  3191  		if auth := r.Header.Get("Authorization"); auth != header {
  3192  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, scope2)
  3193  			w.Header().Set("Www-Authenticate", challenge)
  3194  			w.WriteHeader(http.StatusUnauthorized)
  3195  			return
  3196  		}
  3197  		atomic.AddInt64(&successCount2, 1)
  3198  	}))
  3199  	defer ts1.Close()
  3200  	uri2, err := url.Parse(ts2.URL)
  3201  	if err != nil {
  3202  		t.Fatalf("invalid test http server: %v", err)
  3203  	}
  3204  	service2 = uri2.Host
  3205  	client2 := &Client{
  3206  		Credential: StaticCredential(uri2.Host, Credential{
  3207  			Username: username2,
  3208  			Password: password2,
  3209  		}),
  3210  		ForceAttemptOAuth2: true,
  3211  		Cache:              NewCache(),
  3212  	}
  3213  
  3214  	ctx := context.Background()
  3215  	ctx = WithScopesForHost(ctx, uri1.Host, scopes1...)
  3216  	ctx = WithScopesForHost(ctx, uri2.Host, scopes2...)
  3217  	// first request to server 1
  3218  	req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  3219  	if err != nil {
  3220  		t.Fatalf("failed to create test request: %v", err)
  3221  	}
  3222  	resp1, err := client1.Do(req1)
  3223  	if err != nil {
  3224  		t.Fatalf("Client.Do() error = %v", err)
  3225  	}
  3226  	if resp1.StatusCode != http.StatusOK {
  3227  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  3228  	}
  3229  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  3230  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  3231  	}
  3232  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  3233  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  3234  	}
  3235  	if wantAuthCount1++; authCount1 != wantAuthCount1 {
  3236  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  3237  	}
  3238  
  3239  	// first request to server 1
  3240  	req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  3241  	if err != nil {
  3242  		t.Fatalf("failed to create test request: %v", err)
  3243  	}
  3244  	resp2, err := client2.Do(req2)
  3245  	if err != nil {
  3246  		t.Fatalf("Client.Do() error = %v", err)
  3247  	}
  3248  	if resp2.StatusCode != http.StatusOK {
  3249  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  3250  	}
  3251  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  3252  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  3253  	}
  3254  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  3255  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  3256  	}
  3257  	if wantAuthCount2++; authCount2 != wantAuthCount2 {
  3258  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  3259  	}
  3260  
  3261  	// repeated request to server 1
  3262  	// although the actual scope does not match the hinted scopes, the client
  3263  	// with cache cannot avoid a request to obtain a challenge but can prevent
  3264  	// a repeated call to the authorization server.
  3265  	req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil)
  3266  	if err != nil {
  3267  		t.Fatalf("failed to create test request: %v", err)
  3268  	}
  3269  	resp1, err = client1.Do(req1)
  3270  	if err != nil {
  3271  		t.Fatalf("Client.Do() error = %v", err)
  3272  	}
  3273  	if resp1.StatusCode != http.StatusOK {
  3274  		t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK)
  3275  	}
  3276  	if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 {
  3277  		t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1)
  3278  	}
  3279  	if wantSuccessCount1++; successCount1 != wantSuccessCount1 {
  3280  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1)
  3281  	}
  3282  	if authCount1 != wantAuthCount1 {
  3283  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1)
  3284  	}
  3285  
  3286  	// repeated request to server 2
  3287  	// although the actual scope does not match the hinted scopes, the client
  3288  	// with cache cannot avoid a request to obtain a challenge but can prevent
  3289  	// a repeated call to the authorization server.
  3290  	req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil)
  3291  	if err != nil {
  3292  		t.Fatalf("failed to create test request: %v", err)
  3293  	}
  3294  	resp2, err = client2.Do(req2)
  3295  	if err != nil {
  3296  		t.Fatalf("Client.Do() error = %v", err)
  3297  	}
  3298  	if resp2.StatusCode != http.StatusOK {
  3299  		t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK)
  3300  	}
  3301  	if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 {
  3302  		t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2)
  3303  	}
  3304  	if wantSuccessCount2++; successCount2 != wantSuccessCount2 {
  3305  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2)
  3306  	}
  3307  	if authCount2 != wantAuthCount2 {
  3308  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2)
  3309  	}
  3310  }
  3311  
  3312  func TestClient_Do_Invalid_Credential_Basic(t *testing.T) {
  3313  	username := "test_user"
  3314  	password := "test_password"
  3315  	var requestCount, wantRequestCount int64
  3316  	var successCount, wantSuccessCount int64
  3317  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3318  		atomic.AddInt64(&requestCount, 1)
  3319  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3320  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  3321  			w.WriteHeader(http.StatusNotFound)
  3322  			return
  3323  		}
  3324  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
  3325  		if auth := r.Header.Get("Authorization"); auth != header {
  3326  			w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
  3327  			w.WriteHeader(http.StatusUnauthorized)
  3328  			return
  3329  		}
  3330  		atomic.AddInt64(&successCount, 1)
  3331  		t.Error("authentication should fail but succeeded")
  3332  	}))
  3333  	defer ts.Close()
  3334  	uri, err := url.Parse(ts.URL)
  3335  	if err != nil {
  3336  		t.Fatalf("invalid test http server: %v", err)
  3337  	}
  3338  
  3339  	client := &Client{
  3340  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  3341  			if reg != uri.Host {
  3342  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  3343  				t.Error(err)
  3344  				return EmptyCredential, err
  3345  			}
  3346  			return Credential{
  3347  				Username: username,
  3348  				Password: "bad credential",
  3349  			}, nil
  3350  		},
  3351  	}
  3352  
  3353  	// request should fail
  3354  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  3355  	if err != nil {
  3356  		t.Fatalf("failed to create test request: %v", err)
  3357  	}
  3358  	resp, err := client.Do(req)
  3359  	if err != nil {
  3360  		t.Fatalf("Client.Do() error = %v", err)
  3361  	}
  3362  	if resp.StatusCode != http.StatusUnauthorized {
  3363  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusUnauthorized)
  3364  	}
  3365  	if wantRequestCount += 2; requestCount != wantRequestCount {
  3366  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  3367  	}
  3368  	if successCount != wantSuccessCount {
  3369  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  3370  	}
  3371  }
  3372  
  3373  func TestClient_Do_Invalid_Credential_Bearer(t *testing.T) {
  3374  	username := "test_user"
  3375  	password := "test_password"
  3376  	accessToken := "test/access/token"
  3377  	var requestCount, wantRequestCount int64
  3378  	var successCount, wantSuccessCount int64
  3379  	var authCount, wantAuthCount int64
  3380  	var service string
  3381  	scopes := []string{
  3382  		"repository:dst:pull,push",
  3383  		"repository:src:pull",
  3384  	}
  3385  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3386  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3387  			t.Error("unexecuted attempt of authorization service")
  3388  			w.WriteHeader(http.StatusUnauthorized)
  3389  			return
  3390  		}
  3391  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
  3392  		if auth := r.Header.Get("Authorization"); auth != header {
  3393  			atomic.AddInt64(&authCount, 1)
  3394  			w.WriteHeader(http.StatusUnauthorized)
  3395  			return
  3396  		}
  3397  		t.Error("authentication should fail but succeeded")
  3398  	}))
  3399  	defer as.Close()
  3400  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3401  		atomic.AddInt64(&requestCount, 1)
  3402  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3403  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  3404  			w.WriteHeader(http.StatusNotFound)
  3405  			return
  3406  		}
  3407  		header := "Bearer " + accessToken
  3408  		if auth := r.Header.Get("Authorization"); auth != header {
  3409  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  3410  			w.Header().Set("Www-Authenticate", challenge)
  3411  			w.WriteHeader(http.StatusUnauthorized)
  3412  			return
  3413  		}
  3414  		atomic.AddInt64(&successCount, 1)
  3415  		t.Error("authentication should fail but succeeded")
  3416  	}))
  3417  	defer ts.Close()
  3418  	uri, err := url.Parse(ts.URL)
  3419  	if err != nil {
  3420  		t.Fatalf("invalid test http server: %v", err)
  3421  	}
  3422  	service = uri.Host
  3423  
  3424  	client := &Client{
  3425  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  3426  			if reg != uri.Host {
  3427  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  3428  				t.Error(err)
  3429  				return EmptyCredential, err
  3430  			}
  3431  			return Credential{
  3432  				Username: username,
  3433  				Password: "bad credential",
  3434  			}, nil
  3435  		},
  3436  	}
  3437  
  3438  	// request should fail
  3439  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  3440  	if err != nil {
  3441  		t.Fatalf("failed to create test request: %v", err)
  3442  	}
  3443  	_, err = client.Do(req)
  3444  	if err == nil {
  3445  		t.Fatalf("Client.Do() error = %v, wantErr %v", err, true)
  3446  	}
  3447  	if wantRequestCount++; requestCount != wantRequestCount {
  3448  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  3449  	}
  3450  	if successCount != wantSuccessCount {
  3451  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  3452  	}
  3453  	if wantAuthCount++; authCount != wantAuthCount {
  3454  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  3455  	}
  3456  }
  3457  
  3458  func TestClient_Do_Anonymous_Pull(t *testing.T) {
  3459  	accessToken := "test/access/token"
  3460  	var requestCount, wantRequestCount int64
  3461  	var successCount, wantSuccessCount int64
  3462  	var authCount, wantAuthCount int64
  3463  	var service string
  3464  	scope := "repository:test:pull"
  3465  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3466  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3467  			t.Error("unexecuted attempt of authorization service")
  3468  			w.WriteHeader(http.StatusUnauthorized)
  3469  			return
  3470  		}
  3471  		if auth := r.Header.Get("Authorization"); auth != "" {
  3472  			t.Errorf("unexpected auth: got %s, want %s", auth, "")
  3473  			w.WriteHeader(http.StatusUnauthorized)
  3474  			return
  3475  		}
  3476  		if got := r.URL.Query().Get("service"); got != service {
  3477  			t.Errorf("unexpected service: got %s, want %s", got, service)
  3478  			w.WriteHeader(http.StatusUnauthorized)
  3479  			return
  3480  		}
  3481  		if got := r.URL.Query().Get("scope"); got != scope {
  3482  			t.Errorf("unexpected scope: got %s, want %s", got, scope)
  3483  			w.WriteHeader(http.StatusUnauthorized)
  3484  			return
  3485  		}
  3486  
  3487  		atomic.AddInt64(&authCount, 1)
  3488  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  3489  			t.Errorf("failed to write %q: %v", r.URL, err)
  3490  		}
  3491  	}))
  3492  	defer as.Close()
  3493  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3494  		atomic.AddInt64(&requestCount, 1)
  3495  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3496  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  3497  			w.WriteHeader(http.StatusNotFound)
  3498  			return
  3499  		}
  3500  		header := "Bearer " + accessToken
  3501  		if auth := r.Header.Get("Authorization"); auth != header {
  3502  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
  3503  			w.Header().Set("Www-Authenticate", challenge)
  3504  			w.WriteHeader(http.StatusUnauthorized)
  3505  			return
  3506  		}
  3507  		atomic.AddInt64(&successCount, 1)
  3508  	}))
  3509  	defer ts.Close()
  3510  	uri, err := url.Parse(ts.URL)
  3511  	if err != nil {
  3512  		t.Fatalf("invalid test http server: %v", err)
  3513  	}
  3514  	service = uri.Host
  3515  
  3516  	// request with the default client
  3517  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  3518  	if err != nil {
  3519  		t.Fatalf("failed to create test request: %v", err)
  3520  	}
  3521  	resp, err := DefaultClient.Do(req)
  3522  	if err != nil {
  3523  		t.Fatalf("Client.Do() error = %v", err)
  3524  	}
  3525  	if resp.StatusCode != http.StatusOK {
  3526  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  3527  	}
  3528  	if wantRequestCount += 2; requestCount != wantRequestCount {
  3529  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  3530  	}
  3531  	if wantSuccessCount++; successCount != wantSuccessCount {
  3532  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  3533  	}
  3534  	if wantAuthCount++; authCount != wantAuthCount {
  3535  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  3536  	}
  3537  }
  3538  
  3539  func TestClient_Do_Scheme_Change(t *testing.T) {
  3540  	username := "test_user"
  3541  	password := "test_password"
  3542  	accessToken := "test/access/token"
  3543  	var requestCount, wantRequestCount int64
  3544  	var successCount, wantSuccessCount int64
  3545  	var authCount, wantAuthCount int64
  3546  	var service string
  3547  	scope := "repository:test:pull"
  3548  	challengeBearerAuth := true
  3549  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3550  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3551  			t.Error("unexecuted attempt of authorization service")
  3552  			w.WriteHeader(http.StatusUnauthorized)
  3553  			return
  3554  		}
  3555  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
  3556  		if auth := r.Header.Get("Authorization"); auth != header {
  3557  			t.Errorf("unexpected auth: got %s, want %s", auth, header)
  3558  			w.WriteHeader(http.StatusUnauthorized)
  3559  			return
  3560  		}
  3561  		if got := r.URL.Query().Get("service"); got != service {
  3562  			t.Errorf("unexpected service: got %s, want %s", got, service)
  3563  			w.WriteHeader(http.StatusUnauthorized)
  3564  			return
  3565  		}
  3566  		if got := r.URL.Query().Get("scope"); got != scope {
  3567  			t.Errorf("unexpected scope: got %s, want %s", got, scope)
  3568  			w.WriteHeader(http.StatusUnauthorized)
  3569  			return
  3570  		}
  3571  
  3572  		atomic.AddInt64(&authCount, 1)
  3573  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  3574  			t.Errorf("failed to write %q: %v", r.URL, err)
  3575  		}
  3576  	}))
  3577  	defer as.Close()
  3578  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3579  		atomic.AddInt64(&requestCount, 1)
  3580  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  3581  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  3582  			w.WriteHeader(http.StatusNotFound)
  3583  			return
  3584  		}
  3585  		bearerHeader := "Bearer " + accessToken
  3586  		basicHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
  3587  		header := r.Header.Get("Authorization")
  3588  		if (challengeBearerAuth && header != bearerHeader) || (!challengeBearerAuth && header != basicHeader) {
  3589  			var challenge string
  3590  			if challengeBearerAuth {
  3591  				challenge = fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
  3592  			} else {
  3593  				challenge = `Basic realm="Test Server"`
  3594  			}
  3595  			w.Header().Set("Www-Authenticate", challenge)
  3596  			w.WriteHeader(http.StatusUnauthorized)
  3597  			return
  3598  		}
  3599  		atomic.AddInt64(&successCount, 1)
  3600  	}))
  3601  	defer ts.Close()
  3602  	uri, err := url.Parse(ts.URL)
  3603  	if err != nil {
  3604  		t.Fatalf("invalid test http server: %v", err)
  3605  	}
  3606  	service = uri.Host
  3607  
  3608  	client := &Client{
  3609  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  3610  			if reg != uri.Host {
  3611  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  3612  				t.Error(err)
  3613  				return EmptyCredential, err
  3614  			}
  3615  			return Credential{
  3616  				Username: username,
  3617  				Password: password,
  3618  			}, nil
  3619  		},
  3620  		Cache: NewCache(),
  3621  	}
  3622  
  3623  	// request with bearer auth
  3624  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  3625  	if err != nil {
  3626  		t.Fatalf("failed to create test request: %v", err)
  3627  	}
  3628  	resp, err := client.Do(req)
  3629  	if err != nil {
  3630  		t.Fatalf("Client.Do() error = %v", err)
  3631  	}
  3632  	if resp.StatusCode != http.StatusOK {
  3633  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  3634  	}
  3635  	if wantRequestCount += 2; requestCount != wantRequestCount {
  3636  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  3637  	}
  3638  	if wantSuccessCount++; successCount != wantSuccessCount {
  3639  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  3640  	}
  3641  	if wantAuthCount++; authCount != wantAuthCount {
  3642  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  3643  	}
  3644  
  3645  	// change to basic auth
  3646  	challengeBearerAuth = false
  3647  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
  3648  	if err != nil {
  3649  		t.Fatalf("failed to create test request: %v", err)
  3650  	}
  3651  	resp, err = client.Do(req)
  3652  	if err != nil {
  3653  		t.Fatalf("Client.Do() error = %v", err)
  3654  	}
  3655  	if resp.StatusCode != http.StatusOK {
  3656  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  3657  	}
  3658  	if wantRequestCount += 2; requestCount != wantRequestCount {
  3659  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  3660  	}
  3661  	if wantSuccessCount++; successCount != wantSuccessCount {
  3662  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  3663  	}
  3664  	if authCount != wantAuthCount {
  3665  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  3666  	}
  3667  }
  3668  
  3669  func TestStaticCredential(t *testing.T) {
  3670  	tests := []struct {
  3671  		name     string
  3672  		registry string
  3673  		target   string
  3674  		cred     Credential
  3675  		want     Credential
  3676  	}{
  3677  		{
  3678  			name:     "Matched credential for regular registry",
  3679  			registry: "registry.example.com",
  3680  			target:   "registry.example.com",
  3681  			cred: Credential{
  3682  				Username: "username",
  3683  				Password: "password",
  3684  			},
  3685  			want: Credential{
  3686  				Username: "username",
  3687  				Password: "password",
  3688  			},
  3689  		},
  3690  		{
  3691  			name:     "Matched credential for docker.io",
  3692  			registry: "docker.io",
  3693  			target:   "registry-1.docker.io",
  3694  			cred: Credential{
  3695  				Username: "username",
  3696  				Password: "password",
  3697  			},
  3698  			want: Credential{
  3699  				Username: "username",
  3700  				Password: "password",
  3701  			},
  3702  		},
  3703  		{
  3704  			name:     "Mismatched credential for regular registry",
  3705  			registry: "registry.example.com",
  3706  			target:   "whatever.example.com",
  3707  			cred: Credential{
  3708  				Username: "username",
  3709  				Password: "password",
  3710  			},
  3711  			want: EmptyCredential,
  3712  		},
  3713  		{
  3714  			name:     "Mismatched credential for docker.io",
  3715  			registry: "docker.io",
  3716  			target:   "whatever.docker.io",
  3717  			cred: Credential{
  3718  				Username: "username",
  3719  				Password: "password",
  3720  			},
  3721  			want: EmptyCredential,
  3722  		},
  3723  	}
  3724  	for _, tt := range tests {
  3725  		t.Run(tt.name, func(t *testing.T) {
  3726  			client := &Client{
  3727  				Credential: StaticCredential(tt.registry, tt.cred),
  3728  			}
  3729  			ctx := context.Background()
  3730  			got, err := client.Credential(ctx, tt.target)
  3731  			if err != nil {
  3732  				t.Fatal("Client.Credential() error =", err)
  3733  			}
  3734  			if !reflect.DeepEqual(got, tt.want) {
  3735  				t.Errorf("Client.Credential() = %v, want %v", got, tt.want)
  3736  			}
  3737  		})
  3738  	}
  3739  }
  3740  
  3741  func TestClient_StaticCredential_basicAuth(t *testing.T) {
  3742  	testUsername := "username"
  3743  	testPassword := "password"
  3744  
  3745  	// create a test server
  3746  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3747  		path := r.URL.Path
  3748  		if r.Method != http.MethodGet {
  3749  			w.WriteHeader(http.StatusNotFound)
  3750  			t.Fatal("unexpected access")
  3751  		}
  3752  		switch path {
  3753  		case "/basicAuth":
  3754  			wantedAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(testUsername+":"+testPassword))
  3755  			authHeader := r.Header.Get("Authorization")
  3756  			if authHeader != wantedAuthHeader {
  3757  				w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
  3758  				w.WriteHeader(http.StatusUnauthorized)
  3759  			}
  3760  		default:
  3761  			w.WriteHeader(http.StatusNotAcceptable)
  3762  		}
  3763  	}))
  3764  	defer ts.Close()
  3765  	host := ts.URL
  3766  	uri, _ := url.Parse(host)
  3767  	hostAddress := uri.Host
  3768  	basicAuthURL := fmt.Sprintf("%s/basicAuth", host)
  3769  
  3770  	// create a test client with the correct credentials
  3771  	clientValid := &Client{
  3772  		Credential: StaticCredential(hostAddress, Credential{
  3773  			Username: testUsername,
  3774  			Password: testPassword,
  3775  		}),
  3776  	}
  3777  	req, err := http.NewRequest(http.MethodGet, basicAuthURL, nil)
  3778  	if err != nil {
  3779  		t.Fatalf("could not create request, err = %v", err)
  3780  	}
  3781  	respValid, err := clientValid.Do(req)
  3782  	if err != nil {
  3783  		t.Fatalf("could not send request, err = %v", err)
  3784  	}
  3785  	if respValid.StatusCode != 200 {
  3786  		t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode)
  3787  	}
  3788  
  3789  	// create a test client with incorrect credentials
  3790  	clientInvalid := &Client{
  3791  		Credential: StaticCredential(hostAddress, Credential{
  3792  			Username: "foo",
  3793  			Password: "bar",
  3794  		}),
  3795  	}
  3796  	respInvalid, err := clientInvalid.Do(req)
  3797  	if err != nil {
  3798  		t.Fatalf("could not send request, err = %v", err)
  3799  	}
  3800  	if respInvalid.StatusCode != 401 {
  3801  		t.Errorf("incorrect status code: %d, expected 401", respInvalid.StatusCode)
  3802  	}
  3803  }
  3804  
  3805  func TestClient_StaticCredential_withAccessToken(t *testing.T) {
  3806  	var host string
  3807  	testAccessToken := "test/access/token"
  3808  	scope := "repository:test:pull,push"
  3809  
  3810  	// create an authorization server
  3811  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3812  		w.WriteHeader(http.StatusUnauthorized)
  3813  		t.Error("unexecuted attempt of authorization service")
  3814  	}))
  3815  	defer as.Close()
  3816  
  3817  	// create a test server
  3818  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3819  		path := r.URL.Path
  3820  		if r.Method != http.MethodGet {
  3821  			w.WriteHeader(http.StatusNotFound)
  3822  			t.Fatal("unexpected access")
  3823  		}
  3824  		switch path {
  3825  		case "/accessToken":
  3826  			wantedAuthHeader := "Bearer " + testAccessToken
  3827  			if auth := r.Header.Get("Authorization"); auth != wantedAuthHeader {
  3828  				challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, host, scope)
  3829  				w.Header().Set("Www-Authenticate", challenge)
  3830  				w.WriteHeader(http.StatusUnauthorized)
  3831  			}
  3832  		default:
  3833  			w.WriteHeader(http.StatusNotAcceptable)
  3834  		}
  3835  	}))
  3836  	defer ts.Close()
  3837  	host = ts.URL
  3838  	uri, _ := url.Parse(host)
  3839  	hostAddress := uri.Host
  3840  	accessTokenURL := fmt.Sprintf("%s/accessToken", host)
  3841  
  3842  	// create a test client with the correct credentials
  3843  	clientValid := &Client{
  3844  		Credential: StaticCredential(hostAddress, Credential{
  3845  			AccessToken: testAccessToken,
  3846  		}),
  3847  	}
  3848  	req, err := http.NewRequest(http.MethodGet, accessTokenURL, nil)
  3849  	if err != nil {
  3850  		t.Fatalf("could not create request, err = %v", err)
  3851  	}
  3852  	respValid, err := clientValid.Do(req)
  3853  	if err != nil {
  3854  		t.Fatalf("could not send request, err = %v", err)
  3855  	}
  3856  	if respValid.StatusCode != 200 {
  3857  		t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode)
  3858  	}
  3859  
  3860  	// create a test client with incorrect credentials
  3861  	clientInvalid := &Client{
  3862  		Credential: StaticCredential(hostAddress, Credential{
  3863  			AccessToken: "foo",
  3864  		}),
  3865  	}
  3866  	respInvalid, err := clientInvalid.Do(req)
  3867  	if err != nil {
  3868  		t.Fatalf("could not send request, err = %v", err)
  3869  	}
  3870  	if respInvalid.StatusCode != 401 {
  3871  		t.Errorf("incorrect status code: %d, expected 401", respInvalid.StatusCode)
  3872  	}
  3873  }
  3874  
  3875  func TestClient_StaticCredential_withRefreshToken(t *testing.T) {
  3876  	var host string
  3877  	testAccessToken := "test/access/token"
  3878  	testRefreshToken := "test/refresh/token"
  3879  	scope := "repository:test:pull,push"
  3880  
  3881  	// create an authorization server
  3882  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3883  		if r.Method != http.MethodGet && r.Method != http.MethodPost {
  3884  			w.WriteHeader(http.StatusUnauthorized)
  3885  			t.Error("unexecuted attempt of authorization service")
  3886  		}
  3887  		if err := r.ParseForm(); err != nil {
  3888  			w.WriteHeader(http.StatusUnauthorized)
  3889  			t.Error("failed to parse form")
  3890  		}
  3891  		if got := r.PostForm.Get("service"); got != host {
  3892  			w.WriteHeader(http.StatusUnauthorized)
  3893  		}
  3894  		// handles refresh token requests
  3895  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  3896  			w.WriteHeader(http.StatusUnauthorized)
  3897  		}
  3898  		if got := r.PostForm.Get("scope"); got != scope {
  3899  			w.WriteHeader(http.StatusUnauthorized)
  3900  		}
  3901  		if got := r.PostForm.Get("refresh_token"); got != testRefreshToken {
  3902  			w.WriteHeader(http.StatusUnauthorized)
  3903  		}
  3904  		// writes back access token
  3905  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, testAccessToken); err != nil {
  3906  			t.Fatalf("could not write back access token, error = %v", err)
  3907  		}
  3908  	}))
  3909  	defer as.Close()
  3910  
  3911  	// create a test server
  3912  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3913  		path := r.URL.Path
  3914  		if r.Method != http.MethodGet {
  3915  			w.WriteHeader(http.StatusNotFound)
  3916  			panic("unexpected access")
  3917  		}
  3918  		switch path {
  3919  		case "/refreshToken":
  3920  			wantedAuthHeader := "Bearer " + testAccessToken
  3921  			if auth := r.Header.Get("Authorization"); auth != wantedAuthHeader {
  3922  				challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, host, scope)
  3923  				w.Header().Set("Www-Authenticate", challenge)
  3924  				w.WriteHeader(http.StatusUnauthorized)
  3925  			}
  3926  		default:
  3927  			w.WriteHeader(http.StatusNotAcceptable)
  3928  		}
  3929  	}))
  3930  	defer ts.Close()
  3931  	host = ts.URL
  3932  	uri, _ := url.Parse(host)
  3933  	hostAddress := uri.Host
  3934  	refreshTokenURL := fmt.Sprintf("%s/refreshToken", host)
  3935  
  3936  	// create a test client with the correct credentials
  3937  	clientValid := &Client{
  3938  		Credential: StaticCredential(hostAddress, Credential{
  3939  			RefreshToken: testRefreshToken,
  3940  		}),
  3941  	}
  3942  	req, err := http.NewRequest(http.MethodGet, refreshTokenURL, nil)
  3943  	if err != nil {
  3944  		t.Fatalf("could not create request, err = %v", err)
  3945  	}
  3946  	respValid, err := clientValid.Do(req)
  3947  	if err != nil {
  3948  		t.Fatalf("could not send request, err = %v", err)
  3949  	}
  3950  	if respValid.StatusCode != 200 {
  3951  		t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode)
  3952  	}
  3953  
  3954  	// create a test client with incorrect credentials
  3955  	clientInvalid := &Client{
  3956  		Credential: StaticCredential(hostAddress, Credential{
  3957  			RefreshToken: "bar",
  3958  		}),
  3959  	}
  3960  	_, err = clientInvalid.Do(req)
  3961  
  3962  	var expectedError *errcode.ErrorResponse
  3963  	if !errors.As(err, &expectedError) || expectedError.StatusCode != http.StatusUnauthorized {
  3964  		t.Errorf("incorrect error: %v, expected %v", err, expectedError)
  3965  	}
  3966  }
  3967  
  3968  func TestClient_fetchBasicAuth(t *testing.T) {
  3969  	c := &Client{
  3970  		Credential: func(ctx context.Context, registry string) (Credential, error) {
  3971  			return EmptyCredential, nil
  3972  		},
  3973  	}
  3974  	_, err := c.fetchBasicAuth(context.Background(), "")
  3975  	if err != ErrBasicCredentialNotFound {
  3976  		t.Errorf("incorrect error: %v, expected %v", err, ErrBasicCredentialNotFound)
  3977  	}
  3978  }