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
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
iv
gitlab-workhorse
Commits
03b7df08
Commit
03b7df08
authored
Nov 05, 2015
by
Marin Jankovski
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'serve-uploads' into lfs_support
parents
b4ea8f84
2dbb5485
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
161 additions
and
18 deletions
+161
-18
Makefile
Makefile
+1
-1
main_test.go
main_test.go
+68
-1
upstream.go
upstream.go
+25
-16
xsendfile.go
xsendfile.go
+67
-0
No files found.
Makefile
View file @
03b7df08
PREFIX
=
/usr/local
VERSION
=
$(
shell
git describe
)
-
$(
shell
date
-u
+%Y%m%d.%H%M%S
)
gitlab-workhorse
:
main.go upstream.go archive.go git-http.go helpers.go lfs.go
gitlab-workhorse
:
main.go upstream.go archive.go git-http.go helpers.go lfs.go
xsendfile.go
go build
-ldflags
"-X main.Version
${VERSION}
"
-o
gitlab-workhorse
install
:
gitlab-workhorse
...
...
main_test.go
View file @
03b7df08
...
...
@@ -239,6 +239,73 @@ func TestDownloadCacheCreate(t *testing.T) {
}
}
func
TestAllowedXSendfileDownload
(
t
*
testing
.
T
)
{
contentFilename
:=
"my-content"
contentPath
:=
path
.
Join
(
cacheDir
,
contentFilename
)
prepareDownloadDir
(
t
)
// Prepare test server and backend
ts
:=
httptest
.
NewServer
(
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
xSendfileType
:=
r
.
Header
.
Get
(
"X-Sendfile-Type"
);
xSendfileType
!=
"X-Sendfile"
{
t
.
Fatalf
(
`X-Sendfile-Type want "X-Sendfile" got %q`
,
xSendfileType
)
}
w
.
Header
()
.
Set
(
"X-Sendfile"
,
contentPath
)
w
.
Header
()
.
Set
(
"Content-Disposition"
,
fmt
.
Sprintf
(
`attachment; filename="%s"`
,
contentFilename
))
w
.
WriteHeader
(
200
)
}))
defer
ts
.
Close
()
defer
cleanUpProcessGroup
(
startServerOrFail
(
t
,
ts
))
if
err
:=
os
.
MkdirAll
(
cacheDir
,
0755
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
contentBytes
:=
[]
byte
{
'c'
,
'o'
,
'n'
,
't'
,
'e'
,
'n'
,
't'
}
if
err
:=
ioutil
.
WriteFile
(
contentPath
,
contentBytes
,
0644
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
downloadCmd
:=
exec
.
Command
(
"curl"
,
"-J"
,
"-O"
,
fmt
.
Sprintf
(
"http://%s/foo/uploads/bar"
,
servAddr
))
downloadCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
downloadCmd
)
actual
,
err
:=
ioutil
.
ReadFile
(
path
.
Join
(
scratchDir
,
contentFilename
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
bytes
.
Compare
(
actual
,
contentBytes
)
!=
0
{
t
.
Fatal
(
"Unexpected file contents in download"
)
}
}
func
TestDeniedXSendfileDownload
(
t
*
testing
.
T
)
{
contentFilename
:=
"my-content"
prepareDownloadDir
(
t
)
// Prepare test server and backend
ts
:=
httptest
.
NewServer
(
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
xSendfileType
:=
r
.
Header
.
Get
(
"X-Sendfile-Type"
);
xSendfileType
!=
"X-Sendfile"
{
t
.
Fatalf
(
`X-Sendfile-Type want "X-Sendfile" got %q`
,
xSendfileType
)
}
w
.
Header
()
.
Set
(
"Content-Disposition"
,
fmt
.
Sprintf
(
`attachment; filename="%s"`
,
contentFilename
))
w
.
WriteHeader
(
404
)
fmt
.
Fprint
(
w
,
"Denied"
)
}))
defer
ts
.
Close
()
defer
cleanUpProcessGroup
(
startServerOrFail
(
t
,
ts
))
downloadCmd
:=
exec
.
Command
(
"curl"
,
"-J"
,
"-O"
,
fmt
.
Sprintf
(
"http://%s/foo/uploads/bar"
,
servAddr
))
downloadCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
downloadCmd
)
actual
,
err
:=
ioutil
.
ReadFile
(
path
.
Join
(
scratchDir
,
contentFilename
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
bytes
.
Compare
(
actual
,
[]
byte
{
'D'
,
'e'
,
'n'
,
'i'
,
'e'
,
'd'
})
!=
0
{
t
.
Fatal
(
"Unexpected file contents in download"
)
}
}
func
prepareDownloadDir
(
t
*
testing
.
T
)
{
if
err
:=
os
.
RemoveAll
(
scratchDir
);
err
!=
nil
{
t
.
Fatal
(
err
)
...
...
@@ -269,7 +336,7 @@ func testAuthServer(code int, body string) *httptest.Server {
}
func
startServerOrFail
(
t
*
testing
.
T
,
ts
*
httptest
.
Server
)
*
exec
.
Cmd
{
cmd
:=
exec
.
Command
(
"go"
,
"run"
,
"main.go"
,
"upstream.go"
,
"archive.go"
,
"git-http.go"
,
"helpers.go"
,
"lfs.go"
,
fmt
.
Sprintf
(
"-authBackend=%s"
,
ts
.
URL
),
fmt
.
Sprintf
(
"-listenAddr=%s"
,
servAddr
))
cmd
:=
exec
.
Command
(
"go"
,
"run"
,
"main.go"
,
"upstream.go"
,
"archive.go"
,
"git-http.go"
,
"helpers.go"
,
"lfs.go"
,
"xsendfile.go"
,
fmt
.
Sprintf
(
"-authBackend=%s"
,
ts
.
URL
),
fmt
.
Sprintf
(
"-listenAddr=%s"
,
servAddr
))
cmd
.
SysProcAttr
=
&
syscall
.
SysProcAttr
{
Setpgid
:
true
}
cmd
.
Stdout
=
os
.
Stdout
cmd
.
Stderr
=
os
.
Stderr
...
...
upstream.go
View file @
03b7df08
...
...
@@ -66,6 +66,7 @@ var gitServices = [...]gitService{
gitService
{
"GET"
,
regexp
.
MustCompile
(
`/repository/archive.tar.bz2\z`
),
repoPreAuth
,
handleGetArchive
,
"tar.bz2"
},
gitService
{
"PUT"
,
regexp
.
MustCompile
(
`/gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`
),
repoPreAuth
,
handleStoreLfsObject
,
"lfs-object-receive"
},
gitService
{
"GET"
,
regexp
.
MustCompile
(
`/gitlab-lfs/objects/([0-9a-f]{64})\z`
),
repoPreAuth
,
handleRetreiveLfsObject
,
"lfs-object-upload"
},
gitService
{
"GET"
,
regexp
.
MustCompile
(
`/uploads/`
),
xSendFile
,
nil
,
""
},
}
func
newUpstream
(
authBackend
string
,
authTransport
http
.
RoundTripper
)
*
upstream
{
...
...
@@ -96,27 +97,13 @@ func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
func
repoPreAuth
(
u
*
upstream
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
handleFunc
func
(
w
http
.
ResponseWriter
,
r
*
gitRequest
,
rpc
string
),
rpc
string
)
{
url
:=
u
.
authBackend
+
r
.
URL
.
RequestURI
()
authReq
,
err
:=
http
.
NewRequest
(
r
.
Method
,
url
,
nil
)
authReq
,
err
:=
u
.
newUpstreamRequest
(
r
)
if
err
!=
nil
{
fail500
(
w
,
"
doAuth
Request"
,
err
)
fail500
(
w
,
"
newUpstream
Request"
,
err
)
return
}
// Forward all headers from our client to the auth backend. This includes
// HTTP Basic authentication credentials (the 'Authorization' header).
for
k
,
v
:=
range
r
.
Header
{
authReq
.
Header
[
k
]
=
v
}
// Also forward the Host header, which is excluded from the Header map by the http libary.
// This allows the Host header received by the backend to be consistent with other
// requests not going through gitlab-workhorse.
authReq
.
Host
=
r
.
Host
// Set a custom header for the request. This can be used in some
// configurations (Passenger) to solve auth request routing problems.
authReq
.
Header
.
Set
(
"GitLab-Git-HTTP-Server"
,
Version
)
authResponse
,
err
:=
u
.
httpClient
.
Do
(
authReq
)
if
err
!=
nil
{
fail500
(
w
,
"doAuthRequest"
,
err
)
return
...
...
@@ -179,3 +166,25 @@ func looksLikeRepo(p string) bool {
}
return
true
}
func
(
u
*
upstream
)
newUpstreamRequest
(
r
*
http
.
Request
)
(
*
http
.
Request
,
error
)
{
url
:=
u
.
authBackend
+
r
.
URL
.
RequestURI
()
authReq
,
err
:=
http
.
NewRequest
(
r
.
Method
,
url
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
// Forward all headers from our client to the auth backend. This includes
// HTTP Basic authentication credentials (the 'Authorization' header).
for
k
,
v
:=
range
r
.
Header
{
authReq
.
Header
[
k
]
=
v
}
// Also forward the Host header, which is excluded from the Header map by the http libary.
// This allows the Host header received by the backend to be consistent with other
// requests not going through gitlab-workhorse.
authReq
.
Host
=
r
.
Host
// Set a custom header for the request. This can be used in some
// configurations (Passenger) to solve auth request routing problems.
authReq
.
Header
.
Set
(
"GitLab-Git-HTTP-Server"
,
Version
)
return
authReq
,
nil
}
xsendfile.go
0 → 100644
View file @
03b7df08
/*
The xSendFile middleware transparently sends static files in HTTP responses
via the X-Sendfile mechanism. All that is needed in the Rails code is the
'send_file' method.
*/
package
main
import
(
"io"
"log"
"net/http"
"os"
)
func
xSendFile
(
u
*
upstream
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
_
func
(
http
.
ResponseWriter
,
*
gitRequest
,
string
),
_
string
)
{
upRequest
,
err
:=
u
.
newUpstreamRequest
(
r
)
if
err
!=
nil
{
fail500
(
w
,
"newUpstreamRequest"
,
err
)
return
}
upRequest
.
Header
.
Set
(
"X-Sendfile-Type"
,
"X-Sendfile"
)
upResponse
,
err
:=
u
.
httpClient
.
Do
(
upRequest
)
if
err
!=
nil
{
fail500
(
w
,
"do upstream request"
,
err
)
return
}
defer
upResponse
.
Body
.
Close
()
// Get X-Sendfile
sendfile
:=
upResponse
.
Header
.
Get
(
"X-Sendfile"
)
upResponse
.
Header
.
Del
(
"X-Sendfile"
)
// Copy headers from Rails upResponse
for
k
,
v
:=
range
upResponse
.
Header
{
w
.
Header
()[
k
]
=
v
}
// Use accelerated file serving
if
sendfile
==
""
&&
upResponse
.
StatusCode
/
100
!=
2
{
// Copy request body otherwise
w
.
WriteHeader
(
upResponse
.
StatusCode
)
// Copy body from Rails upResponse
if
_
,
err
:=
io
.
Copy
(
w
,
upResponse
.
Body
);
err
!=
nil
{
fail500
(
w
,
"Couldn't finalize X-File download request."
,
err
)
}
return
}
log
.
Printf
(
"Serving file %q"
,
sendfile
)
upResponse
.
Body
.
Close
()
content
,
err
:=
os
.
Open
(
sendfile
)
if
err
!=
nil
{
fail500
(
w
,
"open sendfile"
,
err
)
return
}
defer
content
.
Close
()
fi
,
err
:=
content
.
Stat
()
if
err
!=
nil
{
fail500
(
w
,
"xSendFile get mtime"
,
err
)
return
}
http
.
ServeContent
(
w
,
r
,
""
,
fi
.
ModTime
(),
content
)
}
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