Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-workhorse
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kirill Smelkov
gitlab-workhorse
Commits
d33010bb
Commit
d33010bb
authored
Dec 04, 2015
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
X first cut with query
parent
87de16ef
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
37 additions
and
25 deletions
+37
-25
blob.go
blob.go
+37
-25
No files found.
blob.go
View file @
d33010bb
...
@@ -48,6 +48,13 @@ type AuthCacheEntry struct {
...
@@ -48,6 +48,13 @@ type AuthCacheEntry struct {
ready
chan
struct
{}
// closed when entry is ready
ready
chan
struct
{}
// closed when entry is ready
}
}
// Entries are keyed by project + credentials
type
AuthCacheKey
struct
{
project
string
query
string
// e.g. with passing in private_token=...
// TODO headers
}
// Authorization reply cache
// Authorization reply cache
// {} project -> AuthCacheEntry
// {} project -> AuthCacheEntry
//
//
...
@@ -56,22 +63,22 @@ type AuthCache struct {
...
@@ -56,22 +63,22 @@ type AuthCache struct {
u
*
upstream
// for which upstream we cache auth
u
*
upstream
// for which upstream we cache auth
mu
sync
.
RWMutex
// guards .cached
mu
sync
.
RWMutex
// guards .cached
cached
map
[
string
]
*
AuthCacheEntry
cached
map
[
AuthCacheKey
]
*
AuthCacheEntry
}
}
func
NewAuthCache
(
u
*
upstream
)
*
AuthCache
{
func
NewAuthCache
(
u
*
upstream
)
*
AuthCache
{
return
&
AuthCache
{
u
:
u
,
cached
:
make
(
map
[
string
]
*
AuthCacheEntry
)}
return
&
AuthCache
{
u
:
u
,
cached
:
make
(
map
[
AuthCacheKey
]
*
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 != ""
func
(
c
*
AuthCache
)
VerifyDownloadAccess
(
project
string
)
AuthReply
{
func
(
c
*
AuthCache
)
VerifyDownloadAccess
(
key
AuthCacheKey
)
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
c
.
mu
.
RLock
()
c
.
mu
.
RLock
()
auth
:=
c
.
cached
[
project
]
auth
:=
c
.
cached
[
key
]
c
.
mu
.
RUnlock
()
c
.
mu
.
RUnlock
()
have_entry
:
have_entry
:
...
@@ -82,7 +89,7 @@ have_entry:
...
@@ -82,7 +89,7 @@ have_entry:
auth
.
Lock
()
auth
.
Lock
()
auth
.
Nhit
++
auth
.
Nhit
++
//log.Printf("authReply for %v cached ago: %v (hits: %v)",
//log.Printf("authReply for %v cached ago: %v (hits: %v)",
//
project
,
//
key
,
// time.Since(time.Unix(auth.Tauth, 0)),
// time.Since(time.Unix(auth.Tauth, 0)),
// auth.Nhit)
// auth.Nhit)
authReply
=
auth
.
AuthReply
authReply
=
auth
.
AuthReply
...
@@ -95,7 +102,7 @@ have_entry:
...
@@ -95,7 +102,7 @@ have_entry:
c
.
mu
.
Lock
()
c
.
mu
.
Lock
()
// another ex-reader could be trying to create this entry
// another ex-reader could be trying to create this entry
// simultaneously with us - recheck
// simultaneously with us - recheck
auth
=
c
.
cached
[
project
]
auth
=
c
.
cached
[
key
]
if
auth
!=
nil
{
if
auth
!=
nil
{
c
.
mu
.
Unlock
()
c
.
mu
.
Unlock
()
goto
have_entry
goto
have_entry
...
@@ -103,11 +110,11 @@ have_entry:
...
@@ -103,11 +110,11 @@ have_entry:
// new not-yet-ready entry
// new not-yet-ready entry
auth
=
&
AuthCacheEntry
{
ready
:
make
(
chan
struct
{})}
auth
=
&
AuthCacheEntry
{
ready
:
make
(
chan
struct
{})}
c
.
cached
[
project
]
=
auth
c
.
cached
[
key
]
=
auth
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
(
c
.
u
,
project
)
auth
.
AuthReply
=
askAuthBackend
(
c
.
u
,
key
.
project
,
key
.
query
)
auth
.
Tauth
=
time
.
Now
()
.
Unix
()
auth
.
Tauth
=
time
.
Now
()
.
Unix
()
auth
.
Nhit
=
0
auth
.
Nhit
=
0
...
@@ -117,18 +124,18 @@ have_entry:
...
@@ -117,18 +124,18 @@ have_entry:
close
(
auth
.
ready
)
close
(
auth
.
ready
)
// launch entry refresher
// launch entry refresher
go
c
.
refreshEntry
(
auth
,
project
)
go
c
.
refreshEntry
(
auth
,
key
)
}
}
return
authReply
return
authReply
}
}
// Time period for refreshing / removing unused entires in authCache
// Time period for refreshing / removing unused entires in authCache
const
authCacheRefresh
=
30
*
time
.
Second
const
authCacheRefresh
=
5
*
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
,
project
string
)
{
func
(
c
*
AuthCache
)
refreshEntry
(
auth
*
AuthCacheEntry
,
key
AuthCacheKey
)
{
for
{
for
{
time
.
Sleep
(
authCacheRefresh
)
time
.
Sleep
(
authCacheRefresh
)
...
@@ -137,19 +144,19 @@ func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, project string) {
...
@@ -137,19 +144,19 @@ func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, project string) {
auth
.
Unlock
()
auth
.
Unlock
()
// clear cache entry if it is not used
// clear cache entry if it is not used
//log.Printf("AUTH refresh - %v #hit: %v", project
, nhit)
log
.
Printf
(
"AUTH refresh - %v #hit: %v"
,
key
,
nhit
)
if
nhit
==
0
{
// not used - we can remove and stop refreshing
if
nhit
==
0
{
// not used - we can remove and stop refreshing
//log.Printf("AUTH - removing %v", project
)
log
.
Printf
(
"AUTH - removing %v"
,
key
)
// NOTE it is ok even if someone gets this auth in this time window
// NOTE it is ok even if someone gets this auth in this time window
// and use it for some time
// and use it for some time
c
.
mu
.
Lock
()
c
.
mu
.
Lock
()
delete
(
c
.
cached
,
project
)
delete
(
c
.
cached
,
key
)
c
.
mu
.
Unlock
()
c
.
mu
.
Unlock
()
break
break
}
}
//log.Printf("AUTH - refreshing %v", project
)
log
.
Printf
(
"AUTH - refreshing %v"
,
key
)
authReply
:=
askAuthBackend
(
c
.
u
,
project
)
authReply
:=
askAuthBackend
(
c
.
u
,
key
.
project
,
key
.
query
)
auth
.
Lock
()
auth
.
Lock
()
auth
.
AuthReply
=
authReply
auth
.
AuthReply
=
authReply
...
@@ -162,7 +169,7 @@ func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, project string) {
...
@@ -162,7 +169,7 @@ func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, project string) {
// Ask auth backend about whether download is ok for a project.
// Ask auth backend about whether download is ok for a project.
// Authorization is approved if AuthReply.RepoPath != "" on return
// Authorization is approved if AuthReply.RepoPath != "" on return
// Raw auth backend response is emitted to AuthReply.RawReply
// Raw auth backend response is emitted to AuthReply.RawReply
func
askAuthBackend
(
u
*
upstream
,
project
string
)
AuthReply
{
func
askAuthBackend
(
u
*
upstream
,
project
,
query
string
)
AuthReply
{
authReply
:=
AuthReply
{
authReply
:=
AuthReply
{
RawReply
:
httptest
.
NewRecorder
(),
RawReply
:
httptest
.
NewRecorder
(),
}
}
...
@@ -174,8 +181,12 @@ func askAuthBackend(u *upstream, project string) AuthReply {
...
@@ -174,8 +181,12 @@ func askAuthBackend(u *upstream, project string) AuthReply {
// get repo archive XXX
// get repo archive XXX
// XXX private_token not propagated, etc ...
// XXX private_token not propagated, etc ...
// XXX PRIVATE-TOKEN (in header)
// XXX PRIVATE-TOKEN (in header)
//reqDownloadAccess, err := http.NewRequest("GET", project+".git/info/refs?service=git-upload-pack", nil)
// url := project+".git/info/refs?service=git-upload-pack"
reqDownloadAccess
,
err
:=
http
.
NewRequest
(
"GET"
,
project
+
"/repository/archive.zip"
,
nil
)
url
:=
project
+
"/repository/archive.zip"
if
query
!=
""
{
url
+=
"?"
+
query
}
reqDownloadAccess
,
err
:=
http
.
NewRequest
(
"GET"
,
url
,
nil
)
if
err
!=
nil
{
if
err
!=
nil
{
fail500
(
authReply
.
RawReply
,
"GET git-upload-pack"
,
err
)
fail500
(
authReply
.
RawReply
,
"GET git-upload-pack"
,
err
)
return
authReply
return
authReply
...
@@ -199,8 +210,8 @@ func askAuthBackend(u *upstream, project string) AuthReply {
...
@@ -199,8 +210,8 @@ func askAuthBackend(u *upstream, project string) AuthReply {
return
authReply
return
authReply
}
}
func
verifyDownloadAccess
(
u
*
upstream
,
project
string
)
AuthReply
{
func
verifyDownloadAccess
(
u
*
upstream
,
project
,
query
string
)
AuthReply
{
return
u
.
authCache
.
VerifyDownloadAccess
(
project
)
return
u
.
authCache
.
VerifyDownloadAccess
(
AuthCacheKey
{
project
,
query
}
)
}
}
// HTTP handler for `.../raw/<ref>/path`
// HTTP handler for `.../raw/<ref>/path`
...
@@ -209,16 +220,17 @@ var rawRe = regexp.MustCompile(`/raw/`)
...
@@ -209,16 +220,17 @@ var rawRe = regexp.MustCompile(`/raw/`)
func
handleGetBlobRaw
(
w
http
.
ResponseWriter
,
r
*
gitRequest
)
{
func
handleGetBlobRaw
(
w
http
.
ResponseWriter
,
r
*
gitRequest
)
{
// Extract project & refpath
// Extract project & refpath
// <project>/raw/branch/file -> <project>, branch/file
// <project>/raw/branch/file -> <project>, branch/file
rawLoc
:=
rawRe
.
FindStringIndex
(
r
.
Request
.
URL
.
Path
)
url
:=
r
.
Request
.
URL
rawLoc
:=
rawRe
.
FindStringIndex
(
url
.
Path
)
if
rawLoc
==
nil
{
if
rawLoc
==
nil
{
fail500
(
w
,
"extract project name"
,
nil
)
// XXX err=nil
fail500
(
w
,
"extract project name"
,
nil
)
// XXX err=nil
return
return
}
}
project
:=
r
.
Request
.
URL
.
Path
[
:
rawLoc
[
0
]]
project
:=
url
.
Path
[
:
rawLoc
[
0
]]
refpath
:=
r
.
Request
.
URL
.
Path
[
rawLoc
[
1
]
:
]
refpath
:=
url
.
Path
[
rawLoc
[
1
]
:
]
// Query download access auth for this project
// Query download access auth for this project
authReply
:=
verifyDownloadAccess
(
r
.
u
,
project
)
authReply
:=
verifyDownloadAccess
(
r
.
u
,
project
,
url
.
RawQuery
)
if
authReply
.
RepoPath
==
""
{
if
authReply
.
RepoPath
==
""
{
// access denied - copy auth reply to client in full -
// access denied - copy auth reply to client in full -
// there are HTTP code and other headers / body relevant for
// there are HTTP code and other headers / body relevant for
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment