fix simpleAuthChallge concurrent problem

Signed-off-by: spacexnice <yaoyao.xyy@alibaba-inc.com>
This commit is contained in:
spacexnice 2016-08-23 22:09:18 +08:00
parent 4107cdb633
commit 44b8157572
2 changed files with 73 additions and 6 deletions

View file

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"sync"
) )
// Challenge carries information from a WWW-Authenticate response header. // Challenge carries information from a WWW-Authenticate response header.
@ -43,19 +44,26 @@ type ChallengeManager interface {
// perform requests on the endpoints or cache the responses // perform requests on the endpoints or cache the responses
// to a backend. // to a backend.
func NewSimpleChallengeManager() ChallengeManager { func NewSimpleChallengeManager() ChallengeManager {
return simpleChallengeManager{} return &simpleChallengeManager{
Challanges: make(map[string][]Challenge),
}
} }
type simpleChallengeManager map[string][]Challenge type simpleChallengeManager struct{
sync.RWMutex
Challanges map[string][]Challenge
}
func (m simpleChallengeManager) GetChallenges(endpoint url.URL) ([]Challenge, error) { func (m *simpleChallengeManager) GetChallenges(endpoint url.URL) ([]Challenge, error) {
endpoint.Host = strings.ToLower(endpoint.Host) endpoint.Host = strings.ToLower(endpoint.Host)
challenges := m[endpoint.String()] m.RLock()
defer m.RUnlock()
challenges := m.Challanges[endpoint.String()]
return challenges, nil return challenges, nil
} }
func (m simpleChallengeManager) AddResponse(resp *http.Response) error { func (m *simpleChallengeManager) AddResponse(resp *http.Response) error {
challenges := ResponseChallenges(resp) challenges := ResponseChallenges(resp)
if resp.Request == nil { if resp.Request == nil {
return fmt.Errorf("missing request reference") return fmt.Errorf("missing request reference")
@ -65,7 +73,9 @@ func (m simpleChallengeManager) AddResponse(resp *http.Response) error {
Host: strings.ToLower(resp.Request.URL.Host), Host: strings.ToLower(resp.Request.URL.Host),
Scheme: resp.Request.URL.Scheme, Scheme: resp.Request.URL.Scheme,
} }
m[urlCopy.String()] = challenges m.Lock()
defer m.Unlock()
m.Challanges[urlCopy.String()] = challenges
return nil return nil
} }

View file

@ -6,6 +6,7 @@ import (
"net/url" "net/url"
"strings" "strings"
"testing" "testing"
"sync"
) )
func TestAuthChallengeParse(t *testing.T) { func TestAuthChallengeParse(t *testing.T) {
@ -43,6 +44,7 @@ func TestAuthChallengeParse(t *testing.T) {
func TestAuthChallengeNormalization(t *testing.T) { func TestAuthChallengeNormalization(t *testing.T) {
testAuthChallengeNormalization(t, "reg.EXAMPLE.com") testAuthChallengeNormalization(t, "reg.EXAMPLE.com")
testAuthChallengeNormalization(t, "bɿɒʜɔiɿ-ɿɘƚƨim-ƚol-ɒ-ƨʞnɒʜƚ.com") testAuthChallengeNormalization(t, "bɿɒʜɔiɿ-ɿɘƚƨim-ƚol-ɒ-ƨʞnɒʜƚ.com")
testAuthChallengeConcurrent(t, "reg.EXAMPLE.com")
} }
func testAuthChallengeNormalization(t *testing.T, host string) { func testAuthChallengeNormalization(t *testing.T, host string) {
@ -79,3 +81,58 @@ func testAuthChallengeNormalization(t *testing.T, host string) {
t.Fatal("Expected challenge for lower-cased-host URL") t.Fatal("Expected challenge for lower-cased-host URL")
} }
} }
func testAuthChallengeConcurrent(t *testing.T, host string) {
scm := NewSimpleChallengeManager()
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() {
i := 200
for {
//time.Sleep(500 * time.Millisecond)
err = scm.AddResponse(resp)
if err != nil {
t.Fatal(err)
}
i = i -1
if i < 0{
break
}
}
s.Done()
}()
go func() {
lowered := *url
lowered.Host = strings.ToLower(lowered.Host)
k:= 200
for {
_, err := scm.GetChallenges(lowered)
if err != nil {
t.Fatal(err)
}
k = k -1
if k < 0{
break
}
}
s.Done()
}()
s.Wait()
}