Commit 86971fa6 authored by Jacob Vosmaer's avatar Jacob Vosmaer

First steps for downloading uploads

parent ed976a2f
PREFIX=/usr/local PREFIX=/usr/local
VERSION=$(shell git describe)-$(shell date -u +%Y%m%d.%H%M%S) VERSION=$(shell git describe)-$(shell date -u +%Y%m%d.%H%M%S)
gitlab-workhorse: main.go githandler.go archive.go git-http.go helpers.go gitlab-workhorse: main.go githandler.go archive.go git-http.go helpers.go upload.go
go build -ldflags "-X main.Version ${VERSION}" -o gitlab-workhorse go build -ldflags "-X main.Version ${VERSION}" -o gitlab-workhorse
install: gitlab-workhorse install: gitlab-workhorse
......
...@@ -17,6 +17,11 @@ import ( ...@@ -17,6 +17,11 @@ import (
) )
func handleGetArchive(w http.ResponseWriter, r *gitRequest, format string) { func handleGetArchive(w http.ResponseWriter, r *gitRequest, format string) {
if !looksLikeRepo(r.RepoPath) {
http.Error(w, "Not Found", 404)
return
}
archiveFilename := path.Base(r.ArchivePath) archiveFilename := path.Base(r.ArchivePath)
if cachedArchive, err := os.Open(r.ArchivePath); err == nil { if cachedArchive, err := os.Open(r.ArchivePath); err == nil {
......
...@@ -13,6 +13,11 @@ import ( ...@@ -13,6 +13,11 @@ import (
) )
func handleGetInfoRefs(w http.ResponseWriter, r *gitRequest, _ string) { func handleGetInfoRefs(w http.ResponseWriter, r *gitRequest, _ string) {
if !looksLikeRepo(r.RepoPath) {
http.Error(w, "Not Found", 404)
return
}
rpc := r.URL.Query().Get("service") rpc := r.URL.Query().Get("service")
if !(rpc == "git-upload-pack" || rpc == "git-receive-pack") { if !(rpc == "git-upload-pack" || rpc == "git-receive-pack") {
// The 'dumb' Git HTTP protocol is not supported // The 'dumb' Git HTTP protocol is not supported
...@@ -57,6 +62,11 @@ func handleGetInfoRefs(w http.ResponseWriter, r *gitRequest, _ string) { ...@@ -57,6 +62,11 @@ func handleGetInfoRefs(w http.ResponseWriter, r *gitRequest, _ string) {
} }
func handlePostRPC(w http.ResponseWriter, r *gitRequest, rpc string) { func handlePostRPC(w http.ResponseWriter, r *gitRequest, rpc string) {
if !looksLikeRepo(r.RepoPath) {
http.Error(w, "Not Found", 404)
return
}
var body io.ReadCloser var body io.ReadCloser
var err error var err error
......
...@@ -11,8 +11,6 @@ import ( ...@@ -11,8 +11,6 @@ import (
"io" "io"
"log" "log"
"net/http" "net/http"
"os"
"path"
"regexp" "regexp"
"strings" "strings"
) )
...@@ -48,6 +46,10 @@ type gitRequest struct { ...@@ -48,6 +46,10 @@ type gitRequest struct {
// CommitId is used do prevent race conditions between the 'time of check' // 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. // in the GitLab Rails app and the 'time of use' in gitlab-workhorse.
CommitId string CommitId string
// ContentPath is a file on disk we can serve to the client
ContentPath string
// ContentDisposition is used to set the Content-Disposition header
ContentDisposition string
} }
// Routing table // Routing table
...@@ -60,6 +62,7 @@ var gitServices = [...]gitService{ ...@@ -60,6 +62,7 @@ var gitServices = [...]gitService{
gitService{"GET", regexp.MustCompile(`/repository/archive.tar\z`), handleGetArchive, "tar"}, gitService{"GET", regexp.MustCompile(`/repository/archive.tar\z`), handleGetArchive, "tar"},
gitService{"GET", regexp.MustCompile(`/repository/archive.tar.gz\z`), handleGetArchive, "tar.gz"}, gitService{"GET", regexp.MustCompile(`/repository/archive.tar.gz\z`), handleGetArchive, "tar.gz"},
gitService{"GET", regexp.MustCompile(`/repository/archive.tar.bz2\z`), handleGetArchive, "tar.bz2"}, gitService{"GET", regexp.MustCompile(`/repository/archive.tar.bz2\z`), handleGetArchive, "tar.bz2"},
gitService{"GET", regexp.MustCompile(`/uploads/`), handleGetUpload, ""},
} }
func newGitHandler(authBackend string, authTransport http.RoundTripper) *gitHandler { func newGitHandler(authBackend string, authTransport http.RoundTripper) *gitHandler {
...@@ -95,7 +98,7 @@ func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -95,7 +98,7 @@ func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
defer authResponse.Body.Close() defer authResponse.Body.Close()
if authResponse.StatusCode != 200 { if authResponse.StatusCode != 200 || authResponse.Header.Get("GitLab-Workhorse") == "reject" {
// The Git request is not allowed by the backend. Maybe the // The Git request is not allowed by the backend. Maybe the
// client needs to send HTTP Basic credentials. Forward the // client needs to send HTTP Basic credentials. Forward the
// response from the auth backend to our client. This includes // response from the auth backend to our client. This includes
...@@ -134,24 +137,9 @@ func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -134,24 +137,9 @@ func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
if !looksLikeRepo(gitReq.RepoPath) {
http.Error(w, "Not Found", 404)
return
}
g.handleFunc(w, gitReq, g.rpc) g.handleFunc(w, gitReq, g.rpc)
} }
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) { func (h *gitHandler) doAuthRequest(r *http.Request) (result *http.Response, err error) {
url := h.authBackend + r.URL.RequestURI() url := h.authBackend + r.URL.RequestURI()
authReq, err := http.NewRequest(r.Method, url, nil) authReq, err := http.NewRequest(r.Method, url, nil)
......
...@@ -10,9 +10,20 @@ import ( ...@@ -10,9 +10,20 @@ import (
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"path"
"syscall" "syscall"
) )
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 fail500(w http.ResponseWriter, context string, err error) { func fail500(w http.ResponseWriter, context string, err error) {
http.Error(w, "Internal server error", 500) http.Error(w, "Internal server error", 500)
logContext(context, err) logContext(context, err)
......
package main
import (
"fmt"
"net/http"
"os"
"path"
)
func handleGetUpload(w http.ResponseWriter, r *gitRequest, _ string) {
if r.ContentDisposition == "attachment" {
w.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, path.Base(r.ContentPath)))
} else {
w.Header().Add("Content-Disposition", r.ContentDisposition)
}
f, err := os.Open(r.ContentPath)
if err != nil {
logContext("handleGetUpload open file", err)
http.Error(w, "Not Found", 404)
return
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
fail500(w, "handleGetUpload get mtime", err)
return
}
http.ServeContent(w, r.Request, "", fi.ModTime(), f)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment