package challenge import ( "fmt" "net/http" "net/url" "strings" "sync" "testing" ) func TestAuthChallengeParse(t *testing.T) { header := http.Header{} header.Add("WWW-Authenticate", `Bearer realm="https://auth.example.com/token",service="registry.example.com",other=fun,slashed="he\"\l\lo"`) challenges := parseAuthHeader(header) if len(challenges) != 1 { t.Fatalf("Unexpected number of auth challenges: %d, expected 1", len(challenges)) } challenge := challenges[0] if expected := "bearer"; challenge.Scheme != expected { t.Fatalf("Unexpected scheme: %s, expected: %s", challenge.Scheme, expected) } if expected := "https://auth.example.com/token"; challenge.Parameters["realm"] != expected { t.Fatalf("Unexpected param: %s, expected: %s", challenge.Parameters["realm"], expected) } if expected := "registry.example.com"; challenge.Parameters["service"] != expected { t.Fatalf("Unexpected param: %s, expected: %s", challenge.Parameters["service"], expected) } if expected := "fun"; challenge.Parameters["other"] != expected { t.Fatalf("Unexpected param: %s, expected: %s", challenge.Parameters["other"], expected) } if expected := "he\"llo"; challenge.Parameters["slashed"] != expected { t.Fatalf("Unexpected param: %s, expected: %s", challenge.Parameters["slashed"], expected) } } func TestAuthChallengeNormalization(t *testing.T) { testAuthChallengeNormalization(t, "reg.EXAMPLE.com") testAuthChallengeNormalization(t, "bɿɒʜɔiɿ-ɿɘƚƨim-ƚol-ɒ-ƨʞnɒʜƚ.com") testAuthChallengeNormalization(t, "reg.example.com:80") testAuthChallengeConcurrent(t, "reg.EXAMPLE.com") } func testAuthChallengeNormalization(t *testing.T, host string) { scm := NewSimpleManager() url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host)) if err != nil { t.Fatal(err) } resp := &http.Response{ Request: &http.Request{ URL: url, }, Header: make(http.Header), StatusCode: http.StatusUnauthorized, } resp.Header.Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"https://%s/token\",service=\"registry.example.com\"", host)) err = scm.AddResponse(resp) if err != nil { t.Fatal(err) } lowered := *url lowered.Host = strings.ToLower(lowered.Host) lowered.Host = canonicalAddr(&lowered) c, err := scm.GetChallenges(lowered) if err != nil { t.Fatal(err) } if len(c) == 0 { t.Fatal("Expected challenge for lower-cased-host URL") } } func testAuthChallengeConcurrent(t *testing.T, host string) { scm := NewSimpleManager() url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host)) if err != nil { t.Fatal(err) } resp := &http.Response{ Request: &http.Request{ URL: url, }, Header: make(http.Header), StatusCode: http.StatusUnauthorized, } resp.Header.Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"https://%s/token\",service=\"registry.example.com\"", host)) var s sync.WaitGroup s.Add(2) go func() { defer s.Done() for i := 0; i < 200; i++ { err = scm.AddResponse(resp) if err != nil { t.Error(err) } } }() go func() { defer s.Done() lowered := *url lowered.Host = strings.ToLower(lowered.Host) for k := 0; k < 200; k++ { _, err := scm.GetChallenges(lowered) if err != nil { t.Error(err) } } }() s.Wait() }