Commit 2960ec22 authored by Kirill Smelkov's avatar Kirill Smelkov

X AuthCache is now per upstream. Tests pass

parent 4b3b9a15
...@@ -53,17 +53,20 @@ type AuthCacheEntry struct { ...@@ -53,17 +53,20 @@ type AuthCacheEntry struct {
// //
// XXX should be not only project (private_token etc...) // XXX should be not only project (private_token etc...)
type AuthCache struct { type AuthCache struct {
u *upstream // for which upstream we cache auth
mu sync.RWMutex // guards .cached mu sync.RWMutex // guards .cached
cached map[string]*AuthCacheEntry cached map[string]*AuthCacheEntry
} }
var authCache = AuthCache{cached: make(map[string]*AuthCacheEntry)} func NewAuthCache(u *upstream) *AuthCache {
return &AuthCache{u: u, cached: make(map[string]*AuthCacheEntry)}
}
// Verify that download access is ok or not. // Verify that download access is ok or not.
// first we try to use the cache; if information is not there -> ask auth backend // first we try to use the cache; if information is not there -> ask auth backend
// download is ok if AuthReply.RepoPath != "" // download is ok if AuthReply.RepoPath != ""
// XXX u should not be in args? func (c *AuthCache) VerifyDownloadAccess(project string) AuthReply {
func (c *AuthCache) VerifyDownloadAccess(u *upstream, project string) AuthReply {
var authReply AuthReply var authReply AuthReply
// first try to read from cache in parallel with other readers // first try to read from cache in parallel with other readers
...@@ -104,7 +107,7 @@ have_entry: ...@@ -104,7 +107,7 @@ have_entry:
c.mu.Unlock() c.mu.Unlock()
// this goroutine becomes responsible for quering auth backend // this goroutine becomes responsible for quering auth backend
auth.AuthReply = askAuthBackend(u, project) auth.AuthReply = askAuthBackend(c.u, project)
auth.Tauth = time.Now().Unix() auth.Tauth = time.Now().Unix()
auth.Nhit = 0 auth.Nhit = 0
...@@ -114,7 +117,7 @@ have_entry: ...@@ -114,7 +117,7 @@ have_entry:
close(auth.ready) close(auth.ready)
// launch entry refresher // launch entry refresher
go c.refreshEntry(auth, u, project) go c.refreshEntry(auth, project)
} }
return authReply return authReply
...@@ -125,7 +128,7 @@ const authCacheRefresh = 30 * time.Second ...@@ -125,7 +128,7 @@ const authCacheRefresh = 30 * time.Second
// Goroutine to refresh auth cache entry periodically while it is used. // Goroutine to refresh auth cache entry periodically while it is used.
// if the entry is detected to be not used - remove it from cache and stop refreshing. // if the entry is detected to be not used - remove it from cache and stop refreshing.
func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, u *upstream, project string) { func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, project string) {
for { for {
time.Sleep(authCacheRefresh) time.Sleep(authCacheRefresh)
...@@ -146,7 +149,7 @@ func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, u *upstream, project stri ...@@ -146,7 +149,7 @@ func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, u *upstream, project stri
} }
//log.Printf("AUTH - refreshing %v", project) //log.Printf("AUTH - refreshing %v", project)
authReply := askAuthBackend(u, project) authReply := askAuthBackend(c.u, project)
auth.Lock() auth.Lock()
auth.AuthReply = authReply auth.AuthReply = authReply
...@@ -193,7 +196,7 @@ func askAuthBackend(u *upstream, project string) AuthReply { ...@@ -193,7 +196,7 @@ func askAuthBackend(u *upstream, project string) AuthReply {
} }
func verifyDownloadAccess(u *upstream, project string) AuthReply { func verifyDownloadAccess(u *upstream, project string) AuthReply {
return authCache.VerifyDownloadAccess(u, project) return u.authCache.VerifyDownloadAccess(project)
} }
// HTTP handler for `.../raw/<ref>/path` // HTTP handler for `.../raw/<ref>/path`
...@@ -220,9 +223,8 @@ func handleGetBlobRaw(w http.ResponseWriter, r *gitRequest) { ...@@ -220,9 +223,8 @@ func handleGetBlobRaw(w http.ResponseWriter, r *gitRequest) {
w.Header()[k] = v w.Header()[k] = v
} }
w.WriteHeader(authReply.RawReply.Code) w.WriteHeader(authReply.RawReply.Code)
// NOTE do not consume authReply.RawReply.Body with io.Copy - // NOTE do not consume authReply.RawReply.Body with io.Copy() -
// this way it will be read one time only and next reads will // this way it will be read one time only and next reads will be empty.
// be empty.
_, err := w.Write(authReply.RawReply.Body.Bytes()) _, err := w.Write(authReply.RawReply.Body.Bytes())
if err != nil { if err != nil {
logContext("writing authReply.RawReply.Body", err) logContext("writing authReply.RawReply.Body", err)
......
...@@ -20,6 +20,7 @@ type serviceHandleFunc func(w http.ResponseWriter, r *gitRequest) ...@@ -20,6 +20,7 @@ type serviceHandleFunc func(w http.ResponseWriter, r *gitRequest)
type upstream struct { type upstream struct {
httpClient *http.Client httpClient *http.Client
authBackend string authBackend string
authCache *AuthCache
} }
type gitService struct { type gitService struct {
...@@ -89,8 +90,10 @@ var gitServices = [...]gitService{ ...@@ -89,8 +90,10 @@ var gitServices = [...]gitService{
} }
func newUpstream(authBackend string, authTransport http.RoundTripper) *upstream { func newUpstream(authBackend string, authTransport http.RoundTripper) *upstream {
return &upstream{&http.Client{Transport: authTransport}, authBackend} u := &upstream{&http.Client{Transport: authTransport}, authBackend, nil}
u.authCache = NewAuthCache(u)
// XXX Timeout: ... ? // XXX Timeout: ... ?
return u
} }
func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment