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
1
Merge Requests
1
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
nexedi
gitlab-workhorse
Commits
eeb0410d
Commit
eeb0410d
authored
Oct 02, 2015
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
First steps for "git archive" download support
parent
c846662d
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
192 additions
and
9 deletions
+192
-9
githandler.go
githandler.go
+110
-9
main_test.go
main_test.go
+82
-0
No files found.
githandler.go
View file @
eeb0410d
...
...
@@ -34,7 +34,8 @@ type gitService struct {
}
type
gitEnv
struct
{
GL_ID
string
GL_ID
string
ArchivePath
string
}
// Routing table
...
...
@@ -42,6 +43,10 @@ var gitServices = [...]gitService{
gitService
{
"GET"
,
"/info/refs"
,
handleGetInfoRefs
,
""
},
gitService
{
"POST"
,
"/git-upload-pack"
,
handlePostRPC
,
"git-upload-pack"
},
gitService
{
"POST"
,
"/git-receive-pack"
,
handlePostRPC
,
"git-receive-pack"
},
gitService
{
"GET"
,
"/repository/archive.zip"
,
handleGetArchive
,
"zip"
},
gitService
{
"GET"
,
"/repository/archive.tar"
,
handleGetArchive
,
"tar"
},
gitService
{
"GET"
,
"/repository/archive.tar.gz"
,
handleGetArchive
,
"tar.gz"
},
gitService
{
"GET"
,
"/repository/archive.tar.bz2"
,
handleGetArchive
,
"tar.bz2"
},
}
func
newGitHandler
(
repoRoot
,
authBackend
string
)
*
gitHandler
{
...
...
@@ -111,10 +116,6 @@ func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// r.URL.Path does not contain '/../', so there is no possibility
// of path traversal here.
repoPath
:=
path
.
Join
(
h
.
repoRoot
,
strings
.
TrimSuffix
(
r
.
URL
.
Path
,
g
.
suffix
))
if
!
looksLikeRepo
(
repoPath
)
{
http
.
Error
(
w
,
"Not Found"
,
404
)
return
}
g
.
handleFunc
(
env
,
g
.
rpc
,
repoPath
,
w
,
r
)
}
...
...
@@ -143,7 +144,12 @@ func (h *gitHandler) doAuthRequest(r *http.Request) (result *http.Response, err
return
h
.
httpClient
.
Do
(
authReq
)
}
func
handleGetInfoRefs
(
env
gitEnv
,
_
string
,
path
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
func
handleGetInfoRefs
(
env
gitEnv
,
_
string
,
repoPath
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
!
looksLikeRepo
(
repoPath
)
{
http
.
Error
(
w
,
"Not Found"
,
404
)
return
}
rpc
:=
r
.
URL
.
Query
()
.
Get
(
"service"
)
if
!
(
rpc
==
"git-upload-pack"
||
rpc
==
"git-receive-pack"
)
{
// The 'dumb' Git HTTP protocol is not supported
...
...
@@ -152,7 +158,7 @@ func handleGetInfoRefs(env gitEnv, _ string, path string, w http.ResponseWriter,
}
// Prepare our Git subprocess
cmd
:=
gitCommand
(
env
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
"--advertise-refs"
,
p
ath
)
cmd
:=
gitCommand
(
env
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
"--advertise-refs"
,
repoP
ath
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
fail500
(
w
,
"handleGetInfoRefs"
,
err
)
...
...
@@ -187,10 +193,105 @@ func handleGetInfoRefs(env gitEnv, _ string, path string, w http.ResponseWriter,
}
}
func
handlePostRPC
(
env
gitEnv
,
rpc
string
,
path
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
func
handleGetArchive
(
env
gitEnv
,
format
string
,
almostPath
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
ref
:=
r
.
URL
.
Query
()
.
Get
(
"ref"
)
if
ref
==
""
{
ref
=
"HEAD"
}
repoPath
:=
almostPath
+
".git"
if
!
looksLikeRepo
(
repoPath
)
{
http
.
Error
(
w
,
"Not Found"
,
404
)
return
}
var
compressCmd
*
exec
.
Cmd
var
archiveFormat
string
switch
format
{
case
"tar"
:
archiveFormat
=
"tar"
compressCmd
=
nil
case
"tar.gz"
:
archiveFormat
=
"tar"
compressCmd
=
exec
.
Command
(
"gzip"
,
"-c"
)
case
"tar.bz2"
:
archiveFormat
=
"tar"
compressCmd
=
exec
.
Command
(
"bzip2"
,
"-c"
)
case
"zip"
:
archiveFormat
=
"zip"
compressCmd
=
nil
}
archiveCmd
:=
gitCommand
(
env
,
"git"
,
"--git-dir="
+
repoPath
,
"archive"
,
"--format="
+
archiveFormat
,
ref
)
archiveStdout
,
err
:=
archiveCmd
.
StdoutPipe
()
if
err
!=
nil
{
fail500
(
w
,
"handleGetArchive"
,
err
)
return
}
defer
archiveStdout
.
Close
()
if
err
:=
archiveCmd
.
Start
();
err
!=
nil
{
fail500
(
w
,
"handleGetArchive"
,
err
)
return
}
defer
cleanUpProcessGroup
(
archiveCmd
)
// Ensure brute force subprocess clean-up
var
stdout
io
.
ReadCloser
if
compressCmd
==
nil
{
stdout
=
archiveStdout
}
else
{
compressCmd
.
Stdin
=
archiveStdout
stdout
,
err
=
compressCmd
.
StdoutPipe
()
if
err
!=
nil
{
fail500
(
w
,
"handleGetArchive compressCmd stdout pipe"
,
err
)
return
}
defer
stdout
.
Close
()
if
err
:=
compressCmd
.
Start
();
err
!=
nil
{
fail500
(
w
,
"handleGetArchive start compressCmd process"
,
err
)
return
}
defer
compressCmd
.
Wait
()
archiveStdout
.
Close
()
}
// Start writing the response
if
format
==
"zip"
{
w
.
Header
()
.
Add
(
"Content-Type"
,
"application/zip"
)
}
else
{
w
.
Header
()
.
Add
(
"Content-Type"
,
"application/octet-stream"
)
}
w
.
Header
()
.
Add
(
"Content-Transfer-Encoding"
,
"binary"
)
w
.
Header
()
.
Add
(
"Content-Disposition"
,
fmt
.
Sprintf
(
`attachment; filename="%s"`
,
path
.
Base
(
env
.
ArchivePath
)))
w
.
Header
()
.
Add
(
"Cache-Control"
,
"private"
)
w
.
WriteHeader
(
200
)
// Don't bother with HTTP 500 from this point on, just return
if
_
,
err
:=
io
.
Copy
(
w
,
stdout
);
err
!=
nil
{
logContext
(
"handleGetArchive read from subprocess"
,
err
)
return
}
if
err
:=
archiveCmd
.
Wait
();
err
!=
nil
{
logContext
(
"handleGetArchive wait for archiveCmd"
,
err
)
return
}
if
compressCmd
!=
nil
{
if
err
:=
compressCmd
.
Wait
();
err
!=
nil
{
logContext
(
"handleGetArchive wait for compressCmd"
,
err
)
return
}
}
}
func
handlePostRPC
(
env
gitEnv
,
rpc
string
,
repoPath
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
body
io
.
Reader
var
err
error
if
!
looksLikeRepo
(
repoPath
)
{
http
.
Error
(
w
,
"Not Found"
,
404
)
return
}
// The client request body may have been gzipped.
if
r
.
Header
.
Get
(
"Content-Encoding"
)
==
"gzip"
{
body
,
err
=
gzip
.
NewReader
(
r
.
Body
)
...
...
@@ -203,7 +304,7 @@ func handlePostRPC(env gitEnv, rpc string, path string, w http.ResponseWriter, r
}
// Prepare our Git subprocess
cmd
:=
gitCommand
(
env
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
p
ath
)
cmd
:=
gitCommand
(
env
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
repoP
ath
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
fail500
(
w
,
"handlePostRPC"
,
err
)
...
...
main_test.go
View file @
eeb0410d
...
...
@@ -19,6 +19,7 @@ const servWaitSleep = 100 // milliseconds sleep interval
const
scratchDir
=
"test/scratch"
const
testRepoRoot
=
"test/data"
const
testRepo
=
"test.git"
const
testProject
=
"test"
var
remote
=
fmt
.
Sprintf
(
"http://%s/%s"
,
servAddr
,
testRepo
)
var
checkoutDir
=
path
.
Join
(
scratchDir
,
"test"
)
...
...
@@ -96,6 +97,87 @@ func TestDeniedPush(t *testing.T) {
}
}
func
TestAllowedDownloadZip
(
t
*
testing
.
T
)
{
prepareDownloadDir
(
t
)
// Prepare test server and backend
archiveName
:=
"foobar.zip"
ts
:=
testAuthServer
(
200
,
fmt
.
Sprintf
(
`{"ArchivePath":"/tmp/%s"}`
,
archiveName
))
defer
ts
.
Close
()
defer
cleanUpProcessGroup
(
startServerOrFail
(
t
,
ts
))
downloadCmd
:=
exec
.
Command
(
"curl"
,
"-J"
,
"-O"
,
fmt
.
Sprintf
(
"http://%s/%s/repository/archive.zip"
,
servAddr
,
testProject
))
downloadCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
downloadCmd
)
extractCmd
:=
exec
.
Command
(
"unzip"
,
archiveName
)
extractCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
extractCmd
)
}
func
TestAllowedDownloadTar
(
t
*
testing
.
T
)
{
prepareDownloadDir
(
t
)
// Prepare test server and backend
archiveName
:=
"foobar.tar"
ts
:=
testAuthServer
(
200
,
fmt
.
Sprintf
(
`{"ArchivePath":"/tmp/%s"}`
,
archiveName
))
defer
ts
.
Close
()
defer
cleanUpProcessGroup
(
startServerOrFail
(
t
,
ts
))
downloadCmd
:=
exec
.
Command
(
"curl"
,
"-J"
,
"-O"
,
fmt
.
Sprintf
(
"http://%s/%s/repository/archive.tar"
,
servAddr
,
testProject
))
downloadCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
downloadCmd
)
extractCmd
:=
exec
.
Command
(
"tar"
,
"xf"
,
archiveName
)
extractCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
extractCmd
)
}
func
TestAllowedDownloadTarGz
(
t
*
testing
.
T
)
{
prepareDownloadDir
(
t
)
// Prepare test server and backend
archiveName
:=
"foobar.tar.gz"
ts
:=
testAuthServer
(
200
,
fmt
.
Sprintf
(
`{"ArchivePath":"/tmp/%s"}`
,
archiveName
))
defer
ts
.
Close
()
defer
cleanUpProcessGroup
(
startServerOrFail
(
t
,
ts
))
downloadCmd
:=
exec
.
Command
(
"curl"
,
"-J"
,
"-O"
,
fmt
.
Sprintf
(
"http://%s/%s/repository/archive.tar.gz"
,
servAddr
,
testProject
))
downloadCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
downloadCmd
)
extractCmd
:=
exec
.
Command
(
"tar"
,
"zxf"
,
archiveName
)
extractCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
extractCmd
)
}
func
TestAllowedDownloadTarBz2
(
t
*
testing
.
T
)
{
prepareDownloadDir
(
t
)
// Prepare test server and backend
archiveName
:=
"foobar.tar.bz2"
ts
:=
testAuthServer
(
200
,
fmt
.
Sprintf
(
`{"ArchivePath":"/tmp/%s"}`
,
archiveName
))
defer
ts
.
Close
()
defer
cleanUpProcessGroup
(
startServerOrFail
(
t
,
ts
))
downloadCmd
:=
exec
.
Command
(
"curl"
,
"-J"
,
"-O"
,
fmt
.
Sprintf
(
"http://%s/%s/repository/archive.tar.bz2"
,
servAddr
,
testProject
))
downloadCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
downloadCmd
)
extractCmd
:=
exec
.
Command
(
"tar"
,
"jxf"
,
archiveName
)
extractCmd
.
Dir
=
scratchDir
runOrFail
(
t
,
extractCmd
)
}
func
prepareDownloadDir
(
t
*
testing
.
T
)
{
if
err
:=
os
.
RemoveAll
(
scratchDir
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
err
:=
os
.
MkdirAll
(
scratchDir
,
0755
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
}
func
preparePushRepo
(
t
*
testing
.
T
)
{
if
err
:=
os
.
RemoveAll
(
scratchDir
);
err
!=
nil
{
t
.
Fatal
(
err
)
...
...
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