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
9ed11af6
Commit
9ed11af6
authored
Aug 23, 2015
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Put program initialization in a separate file
parent
ceb2092f
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
293 additions
and
276 deletions
+293
-276
README.md
README.md
+1
-1
githandler.go
githandler.go
+286
-0
main.go
main.go
+5
-274
main_test.go
main_test.go
+1
-1
No files found.
README.md
View file @
9ed11af6
...
@@ -31,7 +31,7 @@ You can try out the Git server without authentication as follows:
...
@@ -31,7 +31,7 @@ You can try out the Git server without authentication as follows:
# Start a fake auth backend that allows everything/everybody
# Start a fake auth backend that allows everything/everybody
go run support/say-yes.go &
go run support/say-yes.go &
# Start gitlab-git-http-server
# Start gitlab-git-http-server
go
run main.go
/path/to/git-repos
go
build && ./gitlab-git-http-server
/path/to/git-repos
```
```
Now if you have a Git repository in
`/path/to/git-repos/my-repo.git`
,
Now if you have a Git repository in
`/path/to/git-repos/my-repo.git`
,
...
...
githandler.go
0 → 100644
View file @
9ed11af6
/*
The gitHandler type implements http.Handler.
All code for handling Git HTTP requests is in this file.
*/
package
main
import
(
"compress/gzip"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"os/exec"
"path"
"strings"
"syscall"
)
type
gitHandler
struct
{
httpClient
*
http
.
Client
repoRoot
string
authBackend
string
}
type
gitService
struct
{
method
string
suffix
string
handleFunc
func
(
gitEnv
,
string
,
string
,
http
.
ResponseWriter
,
*
http
.
Request
)
rpc
string
}
type
gitEnv
struct
{
GL_ID
string
}
// Routing table
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"
},
}
func
newGitHandler
(
repoRoot
,
authBackend
string
)
*
gitHandler
{
return
&
gitHandler
{
&
http
.
Client
{},
repoRoot
,
authBackend
}
}
func
(
h
*
gitHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
env
gitEnv
var
g
gitService
log
.
Print
(
r
.
Method
,
" "
,
r
.
URL
)
// Look for a matching Git service
foundService
:=
false
for
_
,
g
=
range
gitServices
{
if
r
.
Method
==
g
.
method
&&
strings
.
HasSuffix
(
r
.
URL
.
Path
,
g
.
suffix
)
{
foundService
=
true
break
}
}
if
!
foundService
{
// The protocol spec in git/Documentation/technical/http-protocol.txt
// says we must return 403 if no matching service is found.
http
.
Error
(
w
,
"Forbidden"
,
403
)
return
}
// Ask the auth backend if the request is allowed, and what the
// user ID (GL_ID) is.
authResponse
,
err
:=
h
.
doAuthRequest
(
r
)
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
authResponse
.
Body
.
Close
()
if
authResponse
.
StatusCode
!=
200
{
// The Git request is not allowed by the backend. Maybe the
// client needs to send HTTP Basic credentials. Forward the
// response from the auth backend to our client. This includes
// the 'WWW-Authentication' header that acts as a hint that
// Basic auth credentials are needed.
for
k
,
v
:=
range
authResponse
.
Header
{
w
.
Header
()[
k
]
=
v
}
w
.
WriteHeader
(
authResponse
.
StatusCode
)
io
.
Copy
(
w
,
authResponse
.
Body
)
return
}
// The auth backend validated the client request and told us who
// the user is according to them (GL_ID). We must extract this
// information from the auth response body.
dec
:=
json
.
NewDecoder
(
authResponse
.
Body
)
if
err
:=
dec
.
Decode
(
&
env
);
err
!=
nil
{
fail500
(
w
,
err
)
return
}
// Don't hog a TCP connection in CLOSE_WAIT, we can already close it now
authResponse
.
Body
.
Close
()
// About path traversal: the Go net/http HTTP server, or
// rather ServeMux, makes the following promise: "ServeMux
// also takes care of sanitizing the URL request path, redirecting
// any request containing . or .. elements to an equivalent
// .- and ..-free URL.". In other words, we may assume that
// 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
)
}
func
looksLikeRepo
(
p
string
)
bool
{
// If /path/to/foo.git/objects exists then let's assume it is a valid Git
// repository.
if
_
,
err
:=
os
.
Stat
(
path
.
Join
(
p
,
"objects"
));
err
!=
nil
{
log
.
Print
(
err
)
return
false
}
return
true
}
func
(
h
*
gitHandler
)
doAuthRequest
(
r
*
http
.
Request
)
(
result
*
http
.
Response
,
err
error
)
{
url
:=
h
.
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
}
return
h
.
httpClient
.
Do
(
authReq
)
}
func
handleGetInfoRefs
(
env
gitEnv
,
_
string
,
path
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
rpc
:=
r
.
URL
.
Query
()
.
Get
(
"service"
)
if
!
(
rpc
==
"git-upload-pack"
||
rpc
==
"git-receive-pack"
)
{
// The 'dumb' Git HTTP protocol is not supported
http
.
Error
(
w
,
"Not Found"
,
404
)
return
}
// Prepare our Git subprocess
cmd
:=
gitCommand
(
env
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
"--advertise-refs"
,
path
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
stdout
.
Close
()
if
err
:=
cmd
.
Start
();
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
cleanUpProcessGroup
(
cmd
)
// Ensure brute force subprocess clean-up
// Start writing the response
w
.
Header
()
.
Add
(
"Content-Type"
,
fmt
.
Sprintf
(
"application/x-%s-advertisement"
,
rpc
))
w
.
Header
()
.
Add
(
"Cache-Control"
,
"no-cache"
)
w
.
WriteHeader
(
200
)
// Don't bother with HTTP 500 from this point on, just panic
if
err
:=
pktLine
(
w
,
fmt
.
Sprintf
(
"# service=%s
\n
"
,
rpc
));
err
!=
nil
{
panic
(
err
)
}
if
err
:=
pktFlush
(
w
);
err
!=
nil
{
panic
(
err
)
}
if
_
,
err
:=
io
.
Copy
(
w
,
stdout
);
err
!=
nil
{
panic
(
err
)
}
if
err
:=
cmd
.
Wait
();
err
!=
nil
{
panic
(
err
)
}
}
func
handlePostRPC
(
env
gitEnv
,
rpc
string
,
path
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
body
io
.
Reader
var
err
error
// The client request body may have been gzipped.
if
r
.
Header
.
Get
(
"Content-Encoding"
)
==
"gzip"
{
body
,
err
=
gzip
.
NewReader
(
r
.
Body
)
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
}
else
{
body
=
r
.
Body
}
// Prepare our Git subprocess
cmd
:=
gitCommand
(
env
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
path
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
stdout
.
Close
()
stdin
,
err
:=
cmd
.
StdinPipe
()
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
stdin
.
Close
()
if
err
:=
cmd
.
Start
();
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
cleanUpProcessGroup
(
cmd
)
// Ensure brute force subprocess clean-up
// Write the client request body to Git's standard input
if
_
,
err
:=
io
.
Copy
(
stdin
,
body
);
err
!=
nil
{
fail500
(
w
,
err
)
return
}
stdin
.
Close
()
// Start writing the response
w
.
Header
()
.
Add
(
"Content-Type"
,
fmt
.
Sprintf
(
"application/x-%s-result"
,
rpc
))
w
.
Header
()
.
Add
(
"Cache-Control"
,
"no-cache"
)
w
.
WriteHeader
(
200
)
// Don't bother with HTTP 500 from this point on, just panic
if
_
,
err
:=
io
.
Copy
(
w
,
stdout
);
err
!=
nil
{
panic
(
err
)
}
if
err
:=
cmd
.
Wait
();
err
!=
nil
{
panic
(
err
)
}
}
func
fail500
(
w
http
.
ResponseWriter
,
err
error
)
{
http
.
Error
(
w
,
"Internal server error"
,
500
)
log
.
Print
(
err
)
}
// Git subprocess helpers
func
subCommand
(
rpc
string
)
string
{
return
strings
.
TrimPrefix
(
rpc
,
"git-"
)
}
func
gitCommand
(
env
gitEnv
,
name
string
,
args
...
string
)
*
exec
.
Cmd
{
cmd
:=
exec
.
Command
(
name
,
args
...
)
// Start the command in its own process group (nice for signalling)
cmd
.
SysProcAttr
=
&
syscall
.
SysProcAttr
{
Setpgid
:
true
}
// Explicitly set the environment for the Git command
cmd
.
Env
=
[]
string
{
fmt
.
Sprintf
(
"PATH=%s"
,
os
.
Getenv
(
"PATH"
)),
fmt
.
Sprintf
(
"GL_ID=%s"
,
env
.
GL_ID
),
}
return
cmd
}
func
cleanUpProcessGroup
(
cmd
*
exec
.
Cmd
)
{
if
cmd
==
nil
{
return
}
process
:=
cmd
.
Process
if
process
!=
nil
&&
process
.
Pid
>
0
{
// Send SIGKILL (kill -9) to the process group of cmd
syscall
.
Kill
(
-
process
.
Pid
,
syscall
.
SIGKILL
)
}
// reap our child process
cmd
.
Wait
()
}
// Git HTTP line protocol functions
func
pktLine
(
w
io
.
Writer
,
s
string
)
error
{
_
,
err
:=
fmt
.
Fprintf
(
w
,
"%04x%s"
,
len
(
s
)
+
4
,
s
)
return
err
}
func
pktFlush
(
w
io
.
Writer
)
error
{
_
,
err
:=
fmt
.
Fprint
(
w
,
"0000"
)
return
err
}
main.go
View file @
9ed11af6
...
@@ -6,58 +6,30 @@ from Git clients that use the 'smart' Git HTTP protocol (git-upload-pack
...
@@ -6,58 +6,30 @@ from Git clients that use the 'smart' Git HTTP protocol (git-upload-pack
and git-receive-pack). It is intended to be deployed behind NGINX
and git-receive-pack). It is intended to be deployed behind NGINX
(for request routing and SSL termination) with access to a GitLab
(for request routing and SSL termination) with access to a GitLab
backend (for authentication and authorization) and local disk access
backend (for authentication and authorization) and local disk access
to Git repositories managed by GitLab.
to Git repositories managed by GitLab. In GitLab, this role was previously
performed by gitlab-grack.
This HTTP server replaces gitlab-grack.
This file contains the main() function. Actual Git HTTP requests are handled by
the gitHandler type, implemented in githandler.go.
*/
*/
package
main
package
main
import
(
import
(
"compress/gzip"
"encoding/json"
"flag"
"flag"
"fmt"
"fmt"
"io"
"log"
"log"
"net"
"net"
"net/http"
"net/http"
"os"
"os"
"os/exec"
"path"
"strings"
"syscall"
"syscall"
)
)
type
gitHandler
struct
{
httpClient
*
http
.
Client
repoRoot
string
authBackend
string
}
type
gitService
struct
{
method
string
suffix
string
handleFunc
func
(
gitEnv
,
string
,
string
,
http
.
ResponseWriter
,
*
http
.
Request
)
rpc
string
}
type
gitEnv
struct
{
GL_ID
string
}
var
Version
string
// Set at build time in the Makefile
var
Version
string
// Set at build time in the Makefile
// Routing table
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"
},
}
func
main
()
{
func
main
()
{
printVersion
:=
flag
.
Bool
(
"version"
,
false
,
"Print version and exit"
)
printVersion
:=
flag
.
Bool
(
"version"
,
false
,
"Print version and exit"
)
listenAddr
:=
flag
.
String
(
"listenAddr"
,
"localhost:8181"
,
"Listen address for HTTP server"
)
listenAddr
:=
flag
.
String
(
"listenAddr"
,
"localhost:8181"
,
"Listen address for HTTP server"
)
listenNetwork
:=
flag
.
String
(
"listenNetwork"
,
"tcp"
,
"Listen 'network' (
protocol
)"
)
listenNetwork
:=
flag
.
String
(
"listenNetwork"
,
"tcp"
,
"Listen 'network' (
tcp, tcp4, tcp6, unix
)"
)
listenUmask
:=
flag
.
Int
(
"listenUmask"
,
022
,
"Umask for Unix socket, default: 022"
)
listenUmask
:=
flag
.
Int
(
"listenUmask"
,
022
,
"Umask for Unix socket, default: 022"
)
authBackend
:=
flag
.
String
(
"authBackend"
,
"http://localhost:8080"
,
"Authentication/authorization backend"
)
authBackend
:=
flag
.
String
(
"authBackend"
,
"http://localhost:8080"
,
"Authentication/authorization backend"
)
flag
.
Usage
=
func
()
{
flag
.
Usage
=
func
()
{
...
@@ -97,244 +69,3 @@ func main() {
...
@@ -97,244 +69,3 @@ func main() {
http
.
Handle
(
"/"
,
newGitHandler
(
repoRoot
,
*
authBackend
))
http
.
Handle
(
"/"
,
newGitHandler
(
repoRoot
,
*
authBackend
))
log
.
Fatal
(
http
.
Serve
(
listener
,
nil
))
log
.
Fatal
(
http
.
Serve
(
listener
,
nil
))
}
}
func
newGitHandler
(
repoRoot
,
authBackend
string
)
*
gitHandler
{
return
&
gitHandler
{
&
http
.
Client
{},
repoRoot
,
authBackend
}
}
func
(
h
*
gitHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
env
gitEnv
var
g
gitService
log
.
Print
(
r
.
Method
,
" "
,
r
.
URL
)
// Look for a matching Git service
foundService
:=
false
for
_
,
g
=
range
gitServices
{
if
r
.
Method
==
g
.
method
&&
strings
.
HasSuffix
(
r
.
URL
.
Path
,
g
.
suffix
)
{
foundService
=
true
break
}
}
if
!
foundService
{
// The protocol spec in git/Documentation/technical/http-protocol.txt
// says we must return 403 if no matching service is found.
http
.
Error
(
w
,
"Forbidden"
,
403
)
return
}
// Ask the auth backend if the request is allowed, and what the
// user ID (GL_ID) is.
authResponse
,
err
:=
h
.
doAuthRequest
(
r
)
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
authResponse
.
Body
.
Close
()
if
authResponse
.
StatusCode
!=
200
{
// The Git request is not allowed by the backend. Maybe the
// client needs to send HTTP Basic credentials. Forward the
// response from the auth backend to our client. This includes
// the 'WWW-Authentication' header that acts as a hint that
// Basic auth credentials are needed.
for
k
,
v
:=
range
authResponse
.
Header
{
w
.
Header
()[
k
]
=
v
}
w
.
WriteHeader
(
authResponse
.
StatusCode
)
io
.
Copy
(
w
,
authResponse
.
Body
)
return
}
// The auth backend validated the client request and told us who
// the user is according to them (GL_ID). We must extract this
// information from the auth response body.
dec
:=
json
.
NewDecoder
(
authResponse
.
Body
)
if
err
:=
dec
.
Decode
(
&
env
);
err
!=
nil
{
fail500
(
w
,
err
)
return
}
// Don't hog a TCP connection in CLOSE_WAIT, we can already close it now
authResponse
.
Body
.
Close
()
// About path traversal: the Go net/http HTTP server, or
// rather ServeMux, makes the following promise: "ServeMux
// also takes care of sanitizing the URL request path, redirecting
// any request containing . or .. elements to an equivalent
// .- and ..-free URL.". In other words, we may assume that
// 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
)
}
func
looksLikeRepo
(
p
string
)
bool
{
// If /path/to/foo.git/objects exists then let's assume it is a valid Git
// repository.
if
_
,
err
:=
os
.
Stat
(
path
.
Join
(
p
,
"objects"
));
err
!=
nil
{
log
.
Print
(
err
)
return
false
}
return
true
}
func
(
h
*
gitHandler
)
doAuthRequest
(
r
*
http
.
Request
)
(
result
*
http
.
Response
,
err
error
)
{
url
:=
h
.
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
}
return
h
.
httpClient
.
Do
(
authReq
)
}
func
handleGetInfoRefs
(
env
gitEnv
,
_
string
,
path
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
rpc
:=
r
.
URL
.
Query
()
.
Get
(
"service"
)
if
!
(
rpc
==
"git-upload-pack"
||
rpc
==
"git-receive-pack"
)
{
// The 'dumb' Git HTTP protocol is not supported
http
.
Error
(
w
,
"Not Found"
,
404
)
return
}
// Prepare our Git subprocess
cmd
:=
gitCommand
(
env
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
"--advertise-refs"
,
path
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
stdout
.
Close
()
if
err
:=
cmd
.
Start
();
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
cleanUpProcessGroup
(
cmd
)
// Ensure brute force subprocess clean-up
// Start writing the response
w
.
Header
()
.
Add
(
"Content-Type"
,
fmt
.
Sprintf
(
"application/x-%s-advertisement"
,
rpc
))
w
.
Header
()
.
Add
(
"Cache-Control"
,
"no-cache"
)
w
.
WriteHeader
(
200
)
// Don't bother with HTTP 500 from this point on, just panic
if
err
:=
pktLine
(
w
,
fmt
.
Sprintf
(
"# service=%s
\n
"
,
rpc
));
err
!=
nil
{
panic
(
err
)
}
if
err
:=
pktFlush
(
w
);
err
!=
nil
{
panic
(
err
)
}
if
_
,
err
:=
io
.
Copy
(
w
,
stdout
);
err
!=
nil
{
panic
(
err
)
}
if
err
:=
cmd
.
Wait
();
err
!=
nil
{
panic
(
err
)
}
}
func
handlePostRPC
(
env
gitEnv
,
rpc
string
,
path
string
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
body
io
.
Reader
var
err
error
// The client request body may have been gzipped.
if
r
.
Header
.
Get
(
"Content-Encoding"
)
==
"gzip"
{
body
,
err
=
gzip
.
NewReader
(
r
.
Body
)
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
}
else
{
body
=
r
.
Body
}
// Prepare our Git subprocess
cmd
:=
gitCommand
(
env
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
path
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
stdout
.
Close
()
stdin
,
err
:=
cmd
.
StdinPipe
()
if
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
stdin
.
Close
()
if
err
:=
cmd
.
Start
();
err
!=
nil
{
fail500
(
w
,
err
)
return
}
defer
cleanUpProcessGroup
(
cmd
)
// Ensure brute force subprocess clean-up
// Write the client request body to Git's standard input
if
_
,
err
:=
io
.
Copy
(
stdin
,
body
);
err
!=
nil
{
fail500
(
w
,
err
)
return
}
stdin
.
Close
()
// Start writing the response
w
.
Header
()
.
Add
(
"Content-Type"
,
fmt
.
Sprintf
(
"application/x-%s-result"
,
rpc
))
w
.
Header
()
.
Add
(
"Cache-Control"
,
"no-cache"
)
w
.
WriteHeader
(
200
)
// Don't bother with HTTP 500 from this point on, just panic
if
_
,
err
:=
io
.
Copy
(
w
,
stdout
);
err
!=
nil
{
panic
(
err
)
}
if
err
:=
cmd
.
Wait
();
err
!=
nil
{
panic
(
err
)
}
}
func
fail500
(
w
http
.
ResponseWriter
,
err
error
)
{
http
.
Error
(
w
,
"Internal server error"
,
500
)
log
.
Print
(
err
)
}
// Git subprocess helpers
func
subCommand
(
rpc
string
)
string
{
return
strings
.
TrimPrefix
(
rpc
,
"git-"
)
}
func
gitCommand
(
env
gitEnv
,
name
string
,
args
...
string
)
*
exec
.
Cmd
{
cmd
:=
exec
.
Command
(
name
,
args
...
)
// Start the command in its own process group (nice for signalling)
cmd
.
SysProcAttr
=
&
syscall
.
SysProcAttr
{
Setpgid
:
true
}
// Explicitly set the environment for the Git command
cmd
.
Env
=
[]
string
{
fmt
.
Sprintf
(
"PATH=%s"
,
os
.
Getenv
(
"PATH"
)),
fmt
.
Sprintf
(
"GL_ID=%s"
,
env
.
GL_ID
),
}
return
cmd
}
func
cleanUpProcessGroup
(
cmd
*
exec
.
Cmd
)
{
if
cmd
==
nil
{
return
}
process
:=
cmd
.
Process
if
process
!=
nil
&&
process
.
Pid
>
0
{
// Send SIGKILL (kill -9) to the process group of cmd
syscall
.
Kill
(
-
process
.
Pid
,
syscall
.
SIGKILL
)
}
// reap our child process
cmd
.
Wait
()
}
// Git HTTP line protocol functions
func
pktLine
(
w
io
.
Writer
,
s
string
)
error
{
_
,
err
:=
fmt
.
Fprintf
(
w
,
"%04x%s"
,
len
(
s
)
+
4
,
s
)
return
err
}
func
pktFlush
(
w
io
.
Writer
)
error
{
_
,
err
:=
fmt
.
Fprint
(
w
,
"0000"
)
return
err
}
main_test.go
View file @
9ed11af6
...
@@ -117,7 +117,7 @@ func testAuthServer(code int, body string) *httptest.Server {
...
@@ -117,7 +117,7 @@ func testAuthServer(code int, body string) *httptest.Server {
}
}
func
startServerOrFail
(
t
*
testing
.
T
,
ts
*
httptest
.
Server
)
*
exec
.
Cmd
{
func
startServerOrFail
(
t
*
testing
.
T
,
ts
*
httptest
.
Server
)
*
exec
.
Cmd
{
cmd
:=
exec
.
Command
(
"go"
,
"run"
,
"main.go"
,
fmt
.
Sprintf
(
"-authBackend=%s"
,
ts
.
URL
),
fmt
.
Sprintf
(
"-listenAddr=%s"
,
servAddr
),
testRepoRoot
)
cmd
:=
exec
.
Command
(
"go"
,
"run"
,
"main.go"
,
"githandler.go"
,
fmt
.
Sprintf
(
"-authBackend=%s"
,
ts
.
URL
),
fmt
.
Sprintf
(
"-listenAddr=%s"
,
servAddr
),
testRepoRoot
)
cmd
.
SysProcAttr
=
&
syscall
.
SysProcAttr
{
Setpgid
:
true
}
cmd
.
SysProcAttr
=
&
syscall
.
SysProcAttr
{
Setpgid
:
true
}
cmd
.
Stdout
=
os
.
Stdout
cmd
.
Stdout
=
os
.
Stdout
cmd
.
Stderr
=
os
.
Stderr
cmd
.
Stderr
=
os
.
Stderr
...
...
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