Commit c99c4302 authored by Marin Jankovski's avatar Marin Jankovski

Implement changes requested in feedback. Add download object test.

parent c165c876
...@@ -13,7 +13,6 @@ import ( ...@@ -13,7 +13,6 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
) )
var ( var (
...@@ -34,7 +33,7 @@ func lfsAuthorizeHandler(handleFunc serviceHandleFunc) serviceHandleFunc { ...@@ -34,7 +33,7 @@ func lfsAuthorizeHandler(handleFunc serviceHandleFunc) serviceHandleFunc {
return return
} }
if r.LfsSize == "" { if r.LfsSize == 0 {
fail500(w, "lfsAuthorizeHandler", errors.New("Lfs object size not specified.")) fail500(w, "lfsAuthorizeHandler", errors.New("Lfs object size not specified."))
return return
} }
...@@ -48,62 +47,47 @@ func lfsAuthorizeHandler(handleFunc serviceHandleFunc) serviceHandleFunc { ...@@ -48,62 +47,47 @@ func lfsAuthorizeHandler(handleFunc serviceHandleFunc) serviceHandleFunc {
} }
handleFunc(w, r) handleFunc(w, r)
}, "") }, "/authorize")
} }
func handleStoreLfsObject(handleFunc serviceHandleFunc) serviceHandleFunc { func handleStoreLfsObject(w http.ResponseWriter, r *gitRequest) {
return func(w http.ResponseWriter, r *gitRequest) { var body io.ReadCloser
oid := r.LfsOid
size := r.LfsSize
var body io.ReadCloser body = r.Body
defer body.Close()
body = r.Body tmpPath := r.StoreLFSPath
defer body.Close() file, err := ioutil.TempFile(tmpPath, r.LfsOid)
if err != nil {
tmpPath := r.StoreLFSPath fail500(w, "Couldn't open tmp file for writing.", err)
file, err := ioutil.TempFile(tmpPath, "") return
if err != nil { }
fail500(w, "Couldn't open tmp file for writing.", err) defer os.Remove(tmpPath)
return defer file.Close()
}
defer os.Remove(tmpPath)
defer file.Close()
hash := sha256.New()
hw := io.MultiWriter(hash, file)
written, err := io.Copy(hw, body)
if err != nil {
fail500(w, "Failed to save received LFS object.", err)
return
}
file.Close()
sizeInt, err := strconv.ParseInt(size, 10, 64) hash := sha256.New()
if err != nil { hw := io.MultiWriter(hash, file)
fail500(w, "Couldn't read size: ", err)
return
}
if written != sizeInt { written, err := io.Copy(hw, body)
fail500(w, "Inconsistent size: ", errSizeMismatch) if err != nil {
return fail500(w, "Failed to save received LFS object.", err)
} return
}
file.Close()
shaStr := hex.EncodeToString(hash.Sum(nil)) if written != r.LfsSize {
if shaStr != oid { fail500(w, "Inconsistent size: ", errSizeMismatch)
fail500(w, "Inconsistent size: ", errSizeMismatch) return
return }
}
r.Header.Set("X-GitLab-Lfs-Tmp", filepath.Base(file.Name()))
handleFunc(w, r) shaStr := hex.EncodeToString(hash.Sum(nil))
if shaStr != r.LfsOid {
fail500(w, "Inconsistent size: ", errSizeMismatch)
return
} }
} r.Header.Set("X-GitLab-Lfs-Tmp", filepath.Base(file.Name()))
func lfsCallback(w http.ResponseWriter, r *gitRequest) { authReq, err := r.u.newUpstreamRequest(r.Request, nil, "")
authReq, err := r.u.newUpstreamRequest(r.Request, nil, "/authorize")
if err != nil { if err != nil {
fail500(w, "newUpstreamRequestlfsCallback", err) fail500(w, "newUpstreamRequestlfsCallback", err)
return return
...@@ -115,5 +99,6 @@ func lfsCallback(w http.ResponseWriter, r *gitRequest) { ...@@ -115,5 +99,6 @@ func lfsCallback(w http.ResponseWriter, r *gitRequest) {
return return
} }
defer authResponse.Body.Close() defer authResponse.Body.Close()
return return
} }
...@@ -241,69 +241,18 @@ func TestDownloadCacheCreate(t *testing.T) { ...@@ -241,69 +241,18 @@ func TestDownloadCacheCreate(t *testing.T) {
func TestAllowedXSendfileDownload(t *testing.T) { func TestAllowedXSendfileDownload(t *testing.T) {
contentFilename := "my-content" contentFilename := "my-content"
contentPath := path.Join(cacheDir, contentFilename) url := fmt.Sprintf("http://%s/foo/uploads/bar", servAddr)
prepareDownloadDir(t) prepareDownloadDir(t)
// Prepare test server and backend allowedXSendfileDownload(t, contentFilename, url)
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) { func TestDeniedXSendfileDownload(t *testing.T) {
contentFilename := "my-content" contentFilename := "my-content"
url := fmt.Sprintf("http://%s/foo/uploads/bar", servAddr)
prepareDownloadDir(t) prepareDownloadDir(t)
// Prepare test server and backend deniedXSendfileDownload(t, contentFilename, url)
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(200)
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) { func prepareDownloadDir(t *testing.T) {
...@@ -401,3 +350,85 @@ func repoPath(t *testing.T) string { ...@@ -401,3 +350,85 @@ func repoPath(t *testing.T) string {
} }
return path.Join(cwd, testRepoRoot, testRepo) return path.Join(cwd, testRepoRoot, testRepo)
} }
func TestDeniedLfsDownload(t *testing.T) {
contentFilename := "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80"
url := fmt.Sprintf("http://%s/gitlab-lfs/objects/%s", servAddr, contentFilename)
prepareDownloadDir(t)
deniedXSendfileDownload(t, contentFilename, url)
}
func TestAllowedLfsDownload(t *testing.T) {
contentFilename := "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80"
url := fmt.Sprintf("http://%s/gitlab-lfs/objects/%s", servAddr, contentFilename)
prepareDownloadDir(t)
allowedXSendfileDownload(t, contentFilename, url)
}
func allowedXSendfileDownload(t *testing.T, contentFilename string, url string) {
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.Header().Set("Content-Type", fmt.Sprintf(`application/octet-stream`))
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", url)
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 deniedXSendfileDownload(t *testing.T, contentFilename string, url string) {
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(200)
fmt.Fprint(w, "Denied")
}))
defer ts.Close()
defer cleanUpProcessGroup(startServerOrFail(t, ts))
downloadCmd := exec.Command("curl", "-J", "-O", url)
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")
}
}
...@@ -50,7 +50,7 @@ type authorizationResponse struct { ...@@ -50,7 +50,7 @@ type authorizationResponse struct {
// LFS object id // LFS object id
LfsOid string LfsOid string
// LFS object size // LFS object size
LfsSize string LfsSize int64
} }
// A gitReqest is an *http.Request decorated with attributes returned by the // A gitReqest is an *http.Request decorated with attributes returned by the
...@@ -72,7 +72,7 @@ var gitServices = [...]gitService{ ...@@ -72,7 +72,7 @@ var gitServices = [...]gitService{
gitService{"GET", regexp.MustCompile(`/repository/archive.tar.gz\z`), repoPreAuthorizeHandler(handleGetArchive)}, gitService{"GET", regexp.MustCompile(`/repository/archive.tar.gz\z`), repoPreAuthorizeHandler(handleGetArchive)},
gitService{"GET", regexp.MustCompile(`/repository/archive.tar.bz2\z`), repoPreAuthorizeHandler(handleGetArchive)}, gitService{"GET", regexp.MustCompile(`/repository/archive.tar.bz2\z`), repoPreAuthorizeHandler(handleGetArchive)},
gitService{"GET", regexp.MustCompile(`/uploads/`), handleSendFile}, gitService{"GET", regexp.MustCompile(`/uploads/`), handleSendFile},
gitService{"PUT", regexp.MustCompile(`/gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`), lfsAuthorizeHandler(handleStoreLfsObject(lfsCallback))}, gitService{"PUT", regexp.MustCompile(`/gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`), lfsAuthorizeHandler(handleStoreLfsObject)},
gitService{"GET", regexp.MustCompile(`/gitlab-lfs/objects/([0-9a-f]{64})\z`), handleSendFile}, gitService{"GET", regexp.MustCompile(`/gitlab-lfs/objects/([0-9a-f]{64})\z`), handleSendFile},
} }
......
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