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
9fd59b22
Commit
9fd59b22
authored
Dec 18, 2015
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use http.HandlerFunc and move API into package
parent
bd748961
Changes
18
Show whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
73 additions
and
212 deletions
+73
-212
archive.go
archive.go
+8
-7
artifacts.go
artifacts.go
+3
-2
authorization.go
authorization.go
+0
-112
authorization_test.go
authorization_test.go
+5
-4
deploy_page.go
deploy_page.go
+1
-1
development.go
development.go
+1
-1
error_pages.go
error_pages.go
+1
-1
error_pages_test.go
error_pages_test.go
+2
-2
git-http.go
git-http.go
+13
-12
handlers.go
handlers.go
+3
-2
helpers.go
helpers.go
+0
-6
internal/helper/helpers.go
internal/helper/helpers.go
+6
-0
lfs.go
lfs.go
+13
-11
main.go
main.go
+0
-6
main_test.go
main_test.go
+3
-2
servefile.go
servefile.go
+2
-2
uploads.go
uploads.go
+4
-3
upstream.go
upstream.go
+8
-38
No files found.
archive.go
View file @
9fd59b22
...
...
@@ -5,6 +5,7 @@ In this file we handle 'git archive' downloads
package
main
import
(
"./internal/api"
"./internal/helper"
"fmt"
"io"
...
...
@@ -18,7 +19,7 @@ import (
"time"
)
func
handleGetArchive
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
apiResponse
)
{
func
handleGetArchive
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
.
Response
)
{
var
format
string
urlPath
:=
r
.
URL
.
Path
switch
filepath
.
Base
(
urlPath
)
{
...
...
@@ -31,7 +32,7 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
case
"archive.tar.bz2"
:
format
=
"tar.bz2"
default
:
f
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: invalid format: %s"
,
urlPath
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: invalid format: %s"
,
urlPath
))
return
}
...
...
@@ -54,7 +55,7 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
// to finalize the cached archive.
tempFile
,
err
:=
prepareArchiveTempfile
(
path
.
Dir
(
a
.
ArchivePath
),
archiveFilename
)
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: create tempfile: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: create tempfile: %v"
,
err
))
return
}
defer
tempFile
.
Close
()
...
...
@@ -65,12 +66,12 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
archiveCmd
:=
gitCommand
(
""
,
"git"
,
"--git-dir="
+
a
.
RepoPath
,
"archive"
,
"--format="
+
archiveFormat
,
"--prefix="
+
a
.
ArchivePrefix
+
"/"
,
a
.
CommitId
)
archiveStdout
,
err
:=
archiveCmd
.
StdoutPipe
()
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: archive stdout: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: archive stdout: %v"
,
err
))
return
}
defer
archiveStdout
.
Close
()
if
err
:=
archiveCmd
.
Start
();
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: start %v: %v"
,
archiveCmd
.
Args
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: start %v: %v"
,
archiveCmd
.
Args
,
err
))
return
}
defer
cleanUpProcessGroup
(
archiveCmd
)
// Ensure brute force subprocess clean-up
...
...
@@ -83,13 +84,13 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
stdout
,
err
=
compressCmd
.
StdoutPipe
()
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: compress stdout: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: compress stdout: %v"
,
err
))
return
}
defer
stdout
.
Close
()
if
err
:=
compressCmd
.
Start
();
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: start %v: %v"
,
compressCmd
.
Args
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleGetArchive: start %v: %v"
,
compressCmd
.
Args
,
err
))
return
}
defer
compressCmd
.
Wait
()
...
...
artifacts.go
View file @
9fd59b22
package
main
import
(
"./internal/api"
"net/http"
)
func
artifactsAuthorizeHandler
(
api
*
API
,
h
httpHandleFunc
)
httpHandle
Func
{
return
api
.
preAuthorizeHandler
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
Response
)
{
func
artifactsAuthorizeHandler
(
myAPI
*
api
.
API
,
h
http
.
HandlerFunc
)
http
.
Handler
Func
{
return
myAPI
.
PreAuthorizeHandler
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
.
Response
)
{
r
.
Header
.
Set
(
tempPathHeader
,
a
.
TempPath
)
h
(
w
,
r
)
},
"/authorize"
)
...
...
authorization.go
deleted
100644 → 0
View file @
bd748961
package
main
import
(
"./internal/proxy"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
)
func
(
api
*
API
)
newUpstreamRequest
(
r
*
http
.
Request
,
body
io
.
Reader
,
suffix
string
)
(
*
http
.
Request
,
error
)
{
url
:=
*
api
.
URL
url
.
Path
=
r
.
URL
.
RequestURI
()
+
suffix
authReq
:=
&
http
.
Request
{
Method
:
r
.
Method
,
URL
:
&
url
,
Header
:
proxy
.
HeaderClone
(
r
.
Header
),
}
if
body
!=
nil
{
authReq
.
Body
=
ioutil
.
NopCloser
(
body
)
}
// Clean some headers when issuing a new request without body
if
body
==
nil
{
authReq
.
Header
.
Del
(
"Content-Type"
)
authReq
.
Header
.
Del
(
"Content-Encoding"
)
authReq
.
Header
.
Del
(
"Content-Length"
)
authReq
.
Header
.
Del
(
"Content-Disposition"
)
authReq
.
Header
.
Del
(
"Accept-Encoding"
)
// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
authReq
.
Header
.
Del
(
"Transfer-Encoding"
)
authReq
.
Header
.
Del
(
"Connection"
)
authReq
.
Header
.
Del
(
"Keep-Alive"
)
authReq
.
Header
.
Del
(
"Proxy-Authenticate"
)
authReq
.
Header
.
Del
(
"Proxy-Authorization"
)
authReq
.
Header
.
Del
(
"Te"
)
authReq
.
Header
.
Del
(
"Trailers"
)
authReq
.
Header
.
Del
(
"Upgrade"
)
}
// 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-Workhorse"
,
Version
)
return
authReq
,
nil
}
func
(
api
*
API
)
preAuthorizeHandler
(
h
serviceHandleFunc
,
suffix
string
)
httpHandleFunc
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
authReq
,
err
:=
api
.
newUpstreamRequest
(
r
,
nil
,
suffix
)
if
err
!=
nil
{
fail500
(
w
,
fmt
.
Errorf
(
"preAuthorizeHandler: newUpstreamRequest: %v"
,
err
))
return
}
authResponse
,
err
:=
api
.
Do
(
authReq
)
if
err
!=
nil
{
fail500
(
w
,
fmt
.
Errorf
(
"preAuthorizeHandler: do %v: %v"
,
authReq
.
URL
.
Path
,
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-Authenticate' header that acts as a hint that
// Basic auth credentials are needed.
for
k
,
v
:=
range
authResponse
.
Header
{
// Accomodate broken clients that do case-sensitive header lookup
if
k
==
"Www-Authenticate"
{
w
.
Header
()[
"WWW-Authenticate"
]
=
v
}
else
{
w
.
Header
()[
k
]
=
v
}
}
w
.
WriteHeader
(
authResponse
.
StatusCode
)
io
.
Copy
(
w
,
authResponse
.
Body
)
return
}
a
:=
&
apiResponse
{}
// The auth backend validated the client request and told us additional
// request metadata. We must extract this information from the auth
// response body.
if
err
:=
json
.
NewDecoder
(
authResponse
.
Body
)
.
Decode
(
a
);
err
!=
nil
{
fail500
(
w
,
fmt
.
Errorf
(
"preAuthorizeHandler: decode authorization response: %v"
,
err
))
return
}
// Don't hog a TCP connection in CLOSE_WAIT, we can already close it now
authResponse
.
Body
.
Close
()
// Negotiate authentication (Kerberos) may need to return a WWW-Authenticate
// header to the client even in case of success as per RFC4559.
for
k
,
v
:=
range
authResponse
.
Header
{
// Case-insensitive comparison as per RFC7230
if
strings
.
EqualFold
(
k
,
"WWW-Authenticate"
)
{
w
.
Header
()[
k
]
=
v
}
}
h
(
w
,
r
,
a
)
}
}
authorization_test.go
View file @
9fd59b22
package
main
import
(
"./internal/api"
"fmt"
"net/http"
"net/http/httptest"
...
...
@@ -8,7 +9,7 @@ import (
"testing"
)
func
okHandler
(
w
http
.
ResponseWriter
,
_
*
http
.
Request
,
_
*
apiResponse
)
{
func
okHandler
(
w
http
.
ResponseWriter
,
_
*
http
.
Request
,
_
*
api
.
Response
)
{
w
.
WriteHeader
(
201
)
fmt
.
Fprint
(
w
,
"{
\"
status
\"
:
\"
ok
\"
}"
)
}
...
...
@@ -26,7 +27,7 @@ func runPreAuthorizeHandler(t *testing.T, suffix string, url *regexp.Regexp, api
api
:=
newUpstream
(
ts
.
URL
,
""
)
.
API
response
:=
httptest
.
NewRecorder
()
api
.
p
reAuthorizeHandler
(
okHandler
,
suffix
)(
response
,
httpRequest
)
api
.
P
reAuthorizeHandler
(
okHandler
,
suffix
)(
response
,
httpRequest
)
assertResponseCode
(
t
,
response
,
expectedCode
)
return
response
}
...
...
@@ -35,7 +36,7 @@ func TestPreAuthorizeHappyPath(t *testing.T) {
runPreAuthorizeHandler
(
t
,
"/authorize"
,
regexp
.
MustCompile
(
`/authorize\z`
),
&
apiResponse
{},
&
api
.
Response
{},
200
,
201
)
}
...
...
@@ -43,7 +44,7 @@ func TestPreAuthorizeSuffix(t *testing.T) {
runPreAuthorizeHandler
(
t
,
"/different-authorize"
,
regexp
.
MustCompile
(
`/authorize\z`
),
&
apiResponse
{},
&
api
.
Response
{},
200
,
404
)
}
...
...
deploy_page.go
View file @
9fd59b22
...
...
@@ -6,7 +6,7 @@ import (
"path/filepath"
)
func
handleDeployPage
(
documentRoot
*
string
,
handler
http
HandleFunc
)
httpHandle
Func
{
func
handleDeployPage
(
documentRoot
*
string
,
handler
http
.
HandlerFunc
)
http
.
Handler
Func
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
deployPage
:=
filepath
.
Join
(
*
documentRoot
,
"index.html"
)
data
,
err
:=
ioutil
.
ReadFile
(
deployPage
)
...
...
development.go
View file @
9fd59b22
...
...
@@ -2,7 +2,7 @@ package main
import
"net/http"
func
handleDevelopmentMode
(
developmentMode
*
bool
,
handler
http
HandleFunc
)
httpHandle
Func
{
func
handleDevelopmentMode
(
developmentMode
*
bool
,
handler
http
.
HandlerFunc
)
http
.
Handler
Func
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
!*
developmentMode
{
http
.
NotFound
(
w
,
r
)
...
...
error_pages.go
View file @
9fd59b22
...
...
@@ -59,7 +59,7 @@ func (s *errorPageResponseWriter) Flush() {
s
.
WriteHeader
(
http
.
StatusOK
)
}
func
handleRailsError
(
documentRoot
*
string
,
handler
http
.
Handler
)
http
Handle
Func
{
func
handleRailsError
(
documentRoot
*
string
,
handler
http
.
Handler
)
http
.
Handler
Func
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
rw
:=
errorPageResponseWriter
{
rw
:
w
,
...
...
error_pages_test.go
View file @
9fd59b22
...
...
@@ -21,7 +21,7 @@ func TestIfErrorPageIsPresented(t *testing.T) {
ioutil
.
WriteFile
(
filepath
.
Join
(
dir
,
"404.html"
),
[]
byte
(
errorPage
),
0600
)
w
:=
httptest
.
NewRecorder
()
h
:=
http
Handle
Func
(
func
(
w
http
.
ResponseWriter
,
_
*
http
.
Request
)
{
h
:=
http
.
Handler
Func
(
func
(
w
http
.
ResponseWriter
,
_
*
http
.
Request
)
{
w
.
WriteHeader
(
404
)
fmt
.
Fprint
(
w
,
"Not Found"
)
})
...
...
@@ -41,7 +41,7 @@ func TestIfErrorPassedIfNoErrorPageIsFound(t *testing.T) {
w
:=
httptest
.
NewRecorder
()
errorResponse
:=
"ERROR"
h
:=
http
Handle
Func
(
func
(
w
http
.
ResponseWriter
,
_
*
http
.
Request
)
{
h
:=
http
.
Handler
Func
(
func
(
w
http
.
ResponseWriter
,
_
*
http
.
Request
)
{
w
.
WriteHeader
(
404
)
fmt
.
Fprint
(
w
,
errorResponse
)
})
...
...
git-http.go
View file @
9fd59b22
...
...
@@ -5,6 +5,7 @@ In this file we handle the Git 'smart HTTP' protocol
package
main
import
(
"./internal/api"
"./internal/helper"
"errors"
"fmt"
...
...
@@ -27,10 +28,10 @@ func looksLikeRepo(p string) bool {
return
true
}
func
repoPreAuthorizeHandler
(
api
*
API
,
handleFunc
serviceHandleFunc
)
httpHandle
Func
{
return
api
.
preAuthorizeHandler
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
Response
)
{
func
repoPreAuthorizeHandler
(
myAPI
*
api
.
API
,
handleFunc
api
.
HandleFunc
)
http
.
Handler
Func
{
return
myAPI
.
PreAuthorizeHandler
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
.
Response
)
{
if
a
.
RepoPath
==
""
{
f
ail500
(
w
,
errors
.
New
(
"repoPreAuthorizeHandler: RepoPath empty"
))
helper
.
F
ail500
(
w
,
errors
.
New
(
"repoPreAuthorizeHandler: RepoPath empty"
))
return
}
...
...
@@ -43,7 +44,7 @@ func repoPreAuthorizeHandler(api *API, handleFunc serviceHandleFunc) httpHandleF
},
""
)
}
func
handleGetInfoRefs
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
apiResponse
)
{
func
handleGetInfoRefs
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
.
Response
)
{
rpc
:=
r
.
URL
.
Query
()
.
Get
(
"service"
)
if
!
(
rpc
==
"git-upload-pack"
||
rpc
==
"git-receive-pack"
)
{
// The 'dumb' Git HTTP protocol is not supported
...
...
@@ -55,12 +56,12 @@ func handleGetInfoRefs(w http.ResponseWriter, r *http.Request, a *apiResponse) {
cmd
:=
gitCommand
(
a
.
GL_ID
,
"git"
,
subCommand
(
rpc
),
"--stateless-rpc"
,
"--advertise-refs"
,
a
.
RepoPath
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleGetInfoRefs: stdout: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleGetInfoRefs: stdout: %v"
,
err
))
return
}
defer
stdout
.
Close
()
if
err
:=
cmd
.
Start
();
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleGetInfoRefs: start %v: %v"
,
cmd
.
Args
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleGetInfoRefs: start %v: %v"
,
cmd
.
Args
,
err
))
return
}
defer
cleanUpProcessGroup
(
cmd
)
// Ensure brute force subprocess clean-up
...
...
@@ -87,14 +88,14 @@ func handleGetInfoRefs(w http.ResponseWriter, r *http.Request, a *apiResponse) {
}
}
func
handlePostRPC
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
apiResponse
)
{
func
handlePostRPC
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
.
Response
)
{
var
err
error
// Get Git action from URL
action
:=
filepath
.
Base
(
r
.
URL
.
Path
)
if
!
(
action
==
"git-upload-pack"
||
action
==
"git-receive-pack"
)
{
// The 'dumb' Git HTTP protocol is not supported
f
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC: unsupported action: %s"
,
r
.
URL
.
Path
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC: unsupported action: %s"
,
r
.
URL
.
Path
))
return
}
...
...
@@ -102,25 +103,25 @@ func handlePostRPC(w http.ResponseWriter, r *http.Request, a *apiResponse) {
cmd
:=
gitCommand
(
a
.
GL_ID
,
"git"
,
subCommand
(
action
),
"--stateless-rpc"
,
a
.
RepoPath
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC: stdout: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC: stdout: %v"
,
err
))
return
}
defer
stdout
.
Close
()
stdin
,
err
:=
cmd
.
StdinPipe
()
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC: stdin: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC: stdin: %v"
,
err
))
return
}
defer
stdin
.
Close
()
if
err
:=
cmd
.
Start
();
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC: start %v: %v"
,
cmd
.
Args
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC: start %v: %v"
,
cmd
.
Args
,
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
,
r
.
Body
);
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC write to %v: %v"
,
cmd
.
Args
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handlePostRPC write to %v: %v"
,
cmd
.
Args
,
err
))
return
}
// Signal to the Git subprocess that no more data is coming
...
...
handlers.go
View file @
9fd59b22
package
main
import
(
"./internal/helper"
"compress/gzip"
"fmt"
"io"
"net/http"
)
func
contentEncodingHandler
(
h
http
HandleFunc
)
httpHandle
Func
{
func
contentEncodingHandler
(
h
http
.
HandlerFunc
)
http
.
Handler
Func
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
body
io
.
ReadCloser
var
err
error
...
...
@@ -24,7 +25,7 @@ func contentEncodingHandler(h httpHandleFunc) httpHandleFunc {
}
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"contentEncodingHandler: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"contentEncodingHandler: %v"
,
err
))
return
}
defer
body
.
Close
()
...
...
helpers.go
View file @
9fd59b22
...
...
@@ -5,7 +5,6 @@ Miscellaneous helpers: logging, errors, subprocesses
package
main
import
(
"./internal/helper"
"fmt"
"net/http"
"os"
...
...
@@ -14,11 +13,6 @@ import (
"syscall"
)
func
fail500
(
w
http
.
ResponseWriter
,
err
error
)
{
http
.
Error
(
w
,
"Internal server error"
,
500
)
helper
.
LogError
(
err
)
}
func
httpError
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
error
string
,
code
int
)
{
if
r
.
ProtoAtLeast
(
1
,
1
)
{
// Force client to disconnect if we render request error
...
...
internal/helper/helpers.go
View file @
9fd59b22
...
...
@@ -3,9 +3,15 @@ package helper
import
(
"errors"
"log"
"net/http"
"os"
)
func
Fail500
(
w
http
.
ResponseWriter
,
err
error
)
{
http
.
Error
(
w
,
"Internal server error"
,
500
)
LogError
(
err
)
}
func
LogError
(
err
error
)
{
log
.
Printf
(
"error: %v"
,
err
)
}
...
...
lfs.go
View file @
9fd59b22
...
...
@@ -5,6 +5,8 @@ In this file we handle git lfs objects downloads and uploads
package
main
import
(
"./internal/api"
"./internal/helper"
"bytes"
"crypto/sha256"
"encoding/hex"
...
...
@@ -17,21 +19,21 @@ import (
"path/filepath"
)
func
lfsAuthorizeHandler
(
api
*
API
,
handleFunc
serviceHandleFunc
)
httpHandle
Func
{
return
api
.
preAuthorizeHandler
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
Response
)
{
func
lfsAuthorizeHandler
(
myAPI
*
api
.
API
,
handleFunc
api
.
HandleFunc
)
http
.
Handler
Func
{
return
myAPI
.
PreAuthorizeHandler
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
.
Response
)
{
if
a
.
StoreLFSPath
==
""
{
f
ail500
(
w
,
errors
.
New
(
"lfsAuthorizeHandler: StoreLFSPath empty"
))
helper
.
F
ail500
(
w
,
errors
.
New
(
"lfsAuthorizeHandler: StoreLFSPath empty"
))
return
}
if
a
.
LfsOid
==
""
{
f
ail500
(
w
,
errors
.
New
(
"lfsAuthorizeHandler: LfsOid empty"
))
helper
.
F
ail500
(
w
,
errors
.
New
(
"lfsAuthorizeHandler: LfsOid empty"
))
return
}
if
err
:=
os
.
MkdirAll
(
a
.
StoreLFSPath
,
0700
);
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"lfsAuthorizeHandler: mkdia StoreLFSPath: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"lfsAuthorizeHandler: mkdia StoreLFSPath: %v"
,
err
))
return
}
...
...
@@ -39,11 +41,11 @@ func lfsAuthorizeHandler(api *API, handleFunc serviceHandleFunc) httpHandleFunc
},
"/authorize"
)
}
func
handleStoreLfsObject
(
h
http
.
Handler
)
service
HandleFunc
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
apiResponse
)
{
func
handleStoreLfsObject
(
h
http
.
Handler
)
api
.
HandleFunc
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
a
*
api
.
Response
)
{
file
,
err
:=
ioutil
.
TempFile
(
a
.
StoreLFSPath
,
a
.
LfsOid
)
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleStoreLfsObject: create tempfile: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleStoreLfsObject: create tempfile: %v"
,
err
))
return
}
defer
os
.
Remove
(
file
.
Name
())
...
...
@@ -54,19 +56,19 @@ func handleStoreLfsObject(h http.Handler) serviceHandleFunc {
written
,
err
:=
io
.
Copy
(
hw
,
r
.
Body
)
if
err
!=
nil
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleStoreLfsObject: write tempfile: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleStoreLfsObject: write tempfile: %v"
,
err
))
return
}
file
.
Close
()
if
written
!=
a
.
LfsSize
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleStoreLfsObject: expected size %d, wrote %d"
,
a
.
LfsSize
,
written
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleStoreLfsObject: expected size %d, wrote %d"
,
a
.
LfsSize
,
written
))
return
}
shaStr
:=
hex
.
EncodeToString
(
hash
.
Sum
(
nil
))
if
shaStr
!=
a
.
LfsOid
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleStoreLfsObject: expected sha256 %s, got %s"
,
a
.
LfsOid
,
shaStr
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleStoreLfsObject: expected sha256 %s, got %s"
,
a
.
LfsOid
,
shaStr
))
return
}
...
...
main.go
View file @
9fd59b22
...
...
@@ -46,12 +46,6 @@ type httpRoute struct {
handler
http
.
Handler
}
type
httpHandleFunc
func
(
http
.
ResponseWriter
,
*
http
.
Request
)
func
(
h
httpHandleFunc
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
h
(
w
,
r
)
}
const
projectPattern
=
`^/[^/]+/[^/]+/`
const
gitProjectPattern
=
`^/[^/]+/[^/]+\.git/`
...
...
main_test.go
View file @
9fd59b22
package
main
import
(
"./internal/api"
"bytes"
"encoding/json"
"fmt"
...
...
@@ -340,7 +341,7 @@ func runOrFail(t *testing.T, cmd *exec.Cmd) {
}
func
gitOkBody
(
t
*
testing
.
T
)
interface
{}
{
return
&
apiResponse
{
return
&
api
.
Response
{
GL_ID
:
"user-123"
,
RepoPath
:
repoPath
(
t
),
}
...
...
@@ -353,7 +354,7 @@ func archiveOkBody(t *testing.T, archiveName string) interface{} {
}
archivePath
:=
path
.
Join
(
cwd
,
cacheDir
,
archiveName
)
return
&
apiResponse
{
return
&
api
.
Response
{
RepoPath
:
repoPath
(
t
),
ArchivePath
:
archivePath
,
CommitId
:
"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd"
,
...
...
servefile.go
View file @
9fd59b22
...
...
@@ -17,13 +17,13 @@ const (
CacheExpireMax
)
func
(
u
*
upstream
)
handleServeFile
(
documentRoot
*
string
,
cache
CacheMode
,
notFoundHandler
http
HandleFunc
)
httpHandle
Func
{
func
(
u
*
upstream
)
handleServeFile
(
documentRoot
*
string
,
cache
CacheMode
,
notFoundHandler
http
.
HandlerFunc
)
http
.
Handler
Func
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
file
:=
filepath
.
Join
(
*
documentRoot
,
u
.
relativeURIPath
(
cleanURIPath
(
r
.
URL
.
Path
)))
// The filepath.Join does Clean traversing directories up
if
!
strings
.
HasPrefix
(
file
,
*
documentRoot
)
{
f
ail500
(
w
,
&
os
.
PathError
{
helper
.
F
ail500
(
w
,
&
os
.
PathError
{
Op
:
"open"
,
Path
:
file
,
Err
:
os
.
ErrInvalid
,
...
...
uploads.go
View file @
9fd59b22
package
main
import
(
"./internal/helper"
"bytes"
"errors"
"fmt"
...
...
@@ -85,11 +86,11 @@ func rewriteFormFilesFromMultipart(r *http.Request, writer *multipart.Writer, te
return
cleanup
,
nil
}
func
handleFileUploads
(
h
http
.
Handler
)
http
Handle
Func
{
func
handleFileUploads
(
h
http
.
Handler
)
http
.
Handler
Func
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
tempPath
:=
r
.
Header
.
Get
(
tempPathHeader
)
if
tempPath
==
""
{
f
ail500
(
w
,
errors
.
New
(
"handleFileUploads: TempPath empty"
))
helper
.
F
ail500
(
w
,
errors
.
New
(
"handleFileUploads: TempPath empty"
))
return
}
r
.
Header
.
Del
(
tempPathHeader
)
...
...
@@ -104,7 +105,7 @@ func handleFileUploads(h http.Handler) httpHandleFunc {
if
err
==
http
.
ErrNotMultipart
{
h
.
ServeHTTP
(
w
,
r
)
}
else
{
f
ail500
(
w
,
fmt
.
Errorf
(
"handleFileUploads: extract files from multipart: %v"
,
err
))
helper
.
F
ail500
(
w
,
fmt
.
Errorf
(
"handleFileUploads: extract files from multipart: %v"
,
err
))
}
return
}
...
...
upstream.go
View file @
9fd59b22
...
...
@@ -7,6 +7,7 @@ In this file we handle request routing and interaction with the authBackend.
package
main
import
(
"./internal/api"
"./internal/proxy"
"fmt"
"log"
...
...
@@ -17,48 +18,13 @@ import (
"time"
)
type
serviceHandleFunc
func
(
http
.
ResponseWriter
,
*
http
.
Request
,
*
apiResponse
)
type
API
struct
{
*
http
.
Client
*
url
.
URL
}
type
upstream
struct
{
API
*
API
API
*
api
.
API
Proxy
*
proxy
.
Proxy
authBackend
string
relativeURLRoot
string
}
type
apiResponse
struct
{
// GL_ID is an environment variable used by gitlab-shell hooks during 'git
// push' and 'git pull'
GL_ID
string
// RepoPath is the full path on disk to the Git repository the request is
// about
RepoPath
string
// ArchivePath is the full path where we should find/create a cached copy
// of a requested archive
ArchivePath
string
// ArchivePrefix is used to put extracted archive contents in a
// subdirectory
ArchivePrefix
string
// CommitId is used do prevent race conditions between the 'time of check'
// in the GitLab Rails app and the 'time of use' in gitlab-workhorse.
CommitId
string
// StoreLFSPath is provided by the GitLab Rails application
// to mark where the tmp file should be placed
StoreLFSPath
string
// LFS object id
LfsOid
string
// LFS object size
LfsSize
int64
// TmpPath is the path where we should store temporary files
// This is set by authorization middleware
TempPath
string
}
func
newUpstream
(
authBackend
string
,
authSocket
string
)
*
upstream
{
parsedURL
,
err
:=
url
.
Parse
(
authBackend
)
if
err
!=
nil
{
...
...
@@ -89,7 +55,11 @@ func newUpstream(authBackend string, authSocket string) *upstream {
up
:=
&
upstream
{
authBackend
:
authBackend
,
API
:
&
API
{
Client
:
&
http
.
Client
{
Transport
:
proxyTransport
},
URL
:
parsedURL
},
API
:
&
api
.
API
{
Client
:
&
http
.
Client
{
Transport
:
proxyTransport
},
URL
:
parsedURL
,
Version
:
Version
,
},
Proxy
:
proxy
.
NewProxy
(
parsedURL
,
proxyTransport
,
Version
),
relativeURLRoot
:
relativeURLRoot
,
}
...
...
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