Commit 4257a7d0 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Create cached archive while streaming to client

parent d7116ae4
......@@ -11,6 +11,7 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
......@@ -216,13 +217,27 @@ func handleGetArchive(env gitEnv, format string, repoPath string, w http.Respons
w.Header().Add("Content-Transfer-Encoding", "binary")
w.Header().Add("Cache-Control", "private")
if f, err := os.Open(env.ArchivePath); err == nil {
defer f.Close()
if cachedArchive, err := os.Open(env.ArchivePath); err == nil {
defer cachedArchive.Close()
log.Printf("Serving cached file %q", env.ArchivePath)
http.ServeContent(w, r, archiveFilename, time.Unix(0, 0), f)
http.ServeContent(w, r, "", time.Unix(0, 0), cachedArchive)
return
}
// Prepare tempfile to create cached archive
cacheDir := path.Dir(env.ArchivePath)
if err := os.MkdirAll(cacheDir, 0700); err != nil {
fail500(w, "handleGetArchive create archive cache directory", err)
return
}
tempFile, err := ioutil.TempFile(cacheDir, archiveFilename)
if err != nil {
fail500(w, "handleGetArchive create tempfile for archive", err)
return
}
defer tempFile.Close()
defer os.Remove(tempFile.Name())
var compressCmd *exec.Cmd
var archiveFormat string
switch format {
......@@ -277,7 +292,7 @@ func handleGetArchive(env gitEnv, format string, repoPath string, w http.Respons
// Start writing the response
w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just return
if _, err := io.Copy(w, stdout); err != nil {
if _, err := io.Copy(w, io.TeeReader(stdout, tempFile)); err != nil {
logContext("handleGetArchive read from subprocess", err)
return
}
......@@ -291,6 +306,16 @@ func handleGetArchive(env gitEnv, format string, repoPath string, w http.Respons
return
}
}
// Finalize cached archive
if err := tempFile.Close(); err != nil {
logContext("handleGetArchive close cached archive", err)
return
}
if err := os.Link(tempFile.Name(), env.ArchivePath); err != nil {
logContext("handleGetArchive link (finalize) cached archive", err)
return
}
}
func handlePostRPC(env gitEnv, rpc string, repoPath string, w http.ResponseWriter, r *http.Request) {
......
......@@ -220,6 +220,25 @@ func TestDownloadCacheHit(t *testing.T) {
}
}
func TestDownloadCacheCreate(t *testing.T) {
prepareDownloadDir(t)
// Prepare test server and backend
archiveName := "foobar.zip"
ts := testAuthServer(200, archiveOkBody(t, archiveName))
defer ts.Close()
defer cleanUpProcessGroup(startServerOrFail(t, ts))
downloadCmd := exec.Command("curl", "-J", "-O", fmt.Sprintf("http://%s/api/v3/projects/123/repository/archive.zip", servAddr))
downloadCmd.Dir = scratchDir
runOrFail(t, downloadCmd)
compareCmd := exec.Command("cmp", path.Join(cacheDir, archiveName), path.Join(scratchDir, archiveName))
if err := compareCmd.Run(); err != nil {
t.Fatalf("Comparison between downloaded file and cache item failed: %s", err)
}
}
func prepareDownloadDir(t *testing.T) {
if err := os.RemoveAll(scratchDir); err != nil {
t.Fatal(err)
......
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