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

Create cached archive while streaming to client

parent d7116ae4
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"os" "os"
...@@ -216,13 +217,27 @@ func handleGetArchive(env gitEnv, format string, repoPath string, w http.Respons ...@@ -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("Content-Transfer-Encoding", "binary")
w.Header().Add("Cache-Control", "private") w.Header().Add("Cache-Control", "private")
if f, err := os.Open(env.ArchivePath); err == nil { if cachedArchive, err := os.Open(env.ArchivePath); err == nil {
defer f.Close() defer cachedArchive.Close()
log.Printf("Serving cached file %q", env.ArchivePath) 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 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 compressCmd *exec.Cmd
var archiveFormat string var archiveFormat string
switch format { switch format {
...@@ -277,7 +292,7 @@ func handleGetArchive(env gitEnv, format string, repoPath string, w http.Respons ...@@ -277,7 +292,7 @@ func handleGetArchive(env gitEnv, format string, repoPath string, w http.Respons
// Start writing the response // Start writing the response
w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just return 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) logContext("handleGetArchive read from subprocess", err)
return return
} }
...@@ -291,6 +306,16 @@ func handleGetArchive(env gitEnv, format string, repoPath string, w http.Respons ...@@ -291,6 +306,16 @@ func handleGetArchive(env gitEnv, format string, repoPath string, w http.Respons
return 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) { func handlePostRPC(env gitEnv, rpc string, repoPath string, w http.ResponseWriter, r *http.Request) {
......
...@@ -220,6 +220,25 @@ func TestDownloadCacheHit(t *testing.T) { ...@@ -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) { func prepareDownloadDir(t *testing.T) {
if err := os.RemoveAll(scratchDir); err != nil { if err := os.RemoveAll(scratchDir); err != nil {
t.Fatal(err) 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