[DRE-commits] [gitlab-workhorse] 02/03: Imported Upstream version 0.7.2

Dmitry Smirnov onlyjob at moszumanska.debian.org
Mon Jul 18 06:37:38 UTC 2016


This is an automated email from the git hooks/post-receive script.

onlyjob pushed a commit to branch master
in repository gitlab-workhorse.

commit 94bbbda
Author: Dmitry Smirnov <onlyjob at member.fsf.org>
Date:   Mon Jul 18 06:37:02 2016

    Imported Upstream version 0.7.2
---
 .gitignore                                   |  1 +
 CHANGELOG                                    | 23 +++++++++
 Makefile                                     | 36 +++++++++----
 README.md                                    | 39 ++++----------
 VERSION                                      |  2 +-
 authorization_test.go                        |  7 +--
 cmd/gitlab-zip-cat/main.go                   |  5 +-
 cmd/gitlab-zip-metadata/main.go              |  3 +-
 internal/api/api.go                          | 14 ++---
 internal/artifacts/artifact_download.go      |  7 +--
 internal/artifacts/artifact_download_test.go |  7 +--
 internal/artifacts/artifacts_upload.go       |  9 ++--
 internal/artifacts/artifacts_upload_test.go  | 11 ++--
 internal/badgateway/roundtripper.go          |  3 +-
 internal/delay/responsewriter.go             | 76 ++++++++++++++++++++++++++++
 internal/delay/responsewriter_test.go        | 53 +++++++++++++++++++
 internal/git/archive.go                      | 36 +++++++++----
 internal/git/blob.go                         | 42 +++++++--------
 internal/git/git-http.go                     | 30 ++++++-----
 internal/lfs/lfs.go                          | 10 ++--
 internal/proxy/proxy.go                      | 10 ++--
 internal/senddata/injecter.go                | 32 ++++++++++++
 internal/senddata/senddata.go                | 75 +++++++++++++++++++++++++++
 internal/senddata/senddata_test.go           | 20 ++++++++
 internal/{senddata => sendfile}/sendfile.go  | 37 ++++++--------
 internal/staticpages/deploy_page.go          |  3 +-
 internal/staticpages/deploy_page_test.go     |  3 +-
 internal/staticpages/error_pages.go          |  3 +-
 internal/staticpages/error_pages_test.go     |  3 +-
 internal/staticpages/servefile.go            |  5 +-
 internal/staticpages/servefile_test.go       |  3 +-
 internal/upload/uploads.go                   |  3 +-
 internal/upload/uploads_test.go              |  7 +--
 internal/upstream/development_test.go        |  3 +-
 internal/upstream/handlers.go                |  3 +-
 internal/upstream/handlers_test.go           |  3 +-
 internal/upstream/routes.go                  | 41 +++++++--------
 internal/upstream/upstream.go                |  7 +--
 main.go                                      |  5 +-
 main_test.go                                 | 68 ++++++++++++++-----------
 proxy_test.go                                |  9 ++--
 41 files changed, 526 insertions(+), 231 deletions(-)

diff --git a/.gitignore b/.gitignore
index acb538e..9bde789 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ testdata/scratch
 testdata/public
 gitlab-zip-cat
 gitlab-zip-metadata
+_build
diff --git a/CHANGELOG b/CHANGELOG
index 6a8c7f4..fcf30f4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,29 @@
 
 Formerly known as 'gitlab-git-http-server'.
 
+v0.7.2
+
+Integrate with GOPATH during development (remove relative imports
+etc.). Buffer Git HTTP responses so that we may return an error if the
+local command fails early.
+
+v0.7.1
+
+Set Content-Length (retrieved from Git) on raw blob data responses.
+
+v0.7.0
+
+Start using a 'v' prefix on the version string.
+
+0.6.5
+
+Inject 'git archive' data the same way as Git blob data.
+
+0.6.4
+
+Increase default ProxyHeadersTimeout to 5 minutes. Fix injecting raw
+blobs for /api/v3 requetsts.
+
 0.6.3
 
 Add support for sending Git raw git blobs via gitlab-workhorse.
diff --git a/Makefile b/Makefile
index 197ca4b..9b3146d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,33 @@
 PREFIX=/usr/local
 VERSION=$(shell git describe)-$(shell date -u +%Y%m%d.%H%M%S)
+export GOPATH=$(shell pwd)/_build
 GOBUILD=go build -ldflags "-X main.Version=${VERSION}"
+PKG=gitlab.com/gitlab-org/gitlab-workhorse
 
-all: gitlab-zip-cat gitlab-zip-metadata gitlab-workhorse
+all: clean-build gitlab-zip-cat gitlab-zip-metadata gitlab-workhorse
 
-gitlab-zip-cat:	$(shell find cmd/gitlab-zip-cat/ -name '*.go')
-	${GOBUILD} -o $@ ./cmd/$@
+gitlab-zip-cat:	_build $(shell find cmd/gitlab-zip-cat/ -name '*.go')
+	${GOBUILD} -o $@ ${PKG}/cmd/$@
 	
-gitlab-zip-metadata:	$(shell find cmd/gitlab-zip-metadata/ -name '*.go')
-	${GOBUILD} -o $@ ./cmd/$@
+gitlab-zip-metadata:	_build $(shell find cmd/gitlab-zip-metadata/ -name '*.go')
+	${GOBUILD} -o $@ ${PKG}/cmd/$@
 
-gitlab-workhorse: $(shell find . -name '*.go')
-	${GOBUILD} -o $@
+gitlab-workhorse: _build $(shell find . -name '*.go' | grep -v '^\./_')
+	${GOBUILD} -o $@ ${PKG}
 
 install: gitlab-workhorse gitlab-zip-cat gitlab-zip-metadata
 	mkdir -p $(DESTDIR)${PREFIX}/bin/
 	install gitlab-workhorse gitlab-zip-cat gitlab-zip-metadata ${DESTDIR}${PREFIX}/bin/
 
+_build:
+	mkdir -p $@/src/${PKG}
+	tar -cf - --exclude $@ --exclude .git . | (cd $@/src/${PKG} && tar -xf -)
+	touch $@
+
 .PHONY: test
-test: testdata/data/group/test.git clean-workhorse all
-	go fmt ./... | awk '{ print } END { if (NR > 0) { print "Please run go fmt"; exit 1 } }'
-	support/path go test ./...
+test: testdata/data/group/test.git clean-build clean-workhorse all
+	go fmt ${PKG}/... | awk '{ print } END { if (NR > 0) { print "Please run go fmt"; exit 1 } }'
+	support/path go test ${PKG}/...
 	@echo SUCCESS
 
 coverage: testdata/data/group/test.git
@@ -28,6 +35,9 @@ coverage: testdata/data/group/test.git
 	go tool cover -html=test.coverage -o coverage.html
 	rm -f test.coverage
 
+fmt:
+	go fmt ./...
+
 testdata/data/group/test.git: testdata/data
 	git clone --bare https://gitlab.com/gitlab-org/gitlab-test.git $@
 
@@ -35,9 +45,13 @@ testdata/data:
 	mkdir -p $@
 
 .PHONY: clean
-clean:	clean-workhorse
+clean:	clean-workhorse clean-build
 	rm -rf testdata/data testdata/scratch
 
 .PHONY:	clean-workhorse
 clean-workhorse:
 	rm -f gitlab-workhorse gitlab-zip-cat gitlab-zip-metadata
+
+.PHONY:	clean-build
+clean-build:
+	rm -rf _build
diff --git a/README.md b/README.md
index a6a674c..e228cc5 100644
--- a/README.md
+++ b/README.md
@@ -70,42 +70,21 @@ make install PREFIX=/foo
 
 ## Tests
 
-```
-make clean test
-```
-
-## Try it out
-
-You can try out the Git server without authentication as follows:
+Run the tests with:
 
 ```
-# Start a fake auth backend that allows everything/everybody
-make test/data/test.git
-go run support/fake-auth-backend.go ~+/test/data/test.git &
-# Start gitlab-workhorse
-make
-./gitlab-workhorse
+make clean test
 ```
 
-Now you can try things like:
+### Coverage / what to test
 
-```
-git clone http://localhost:8181/test.git
-curl -JO http://localhost:8181/test/repository/archive.zip
-```
+Each feature in gitlab-workhorse should have an integration test that
+verifies that the feature 'kicks in' on the right requests and leaves
+other requests unaffected. It is better to also have package-level tests
+for specific behavior but the high-level integration tests should have
+the first priority during development.
 
-## Example request flow
-
-- start POST repo.git/git-receive-pack to NGINX
-- ..start POST repo.git/git-receive-pack to gitlab-workhorse
-- ....start POST repo.git/git-receive-pack to Unicorn for auth
-- ....end POST to Unicorn for auth
-- ....start git-receive-pack process from gitlab-workhorse
-- ......start POST /api/v3/internal/allowed to Unicorn from Git hook (check protected branches)
-- ......end POST to Unicorn from Git hook
-- ....end git-receive-pack process
-- ..end POST to gitlab-workhorse
-- end POST to NGINX
+It is OK if a feature is only covered by integration tests.
 
 ## License
 
diff --git a/VERSION b/VERSION
index 844f6a9..7486fdb 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6.3
+0.7.2
diff --git a/authorization_test.go b/authorization_test.go
index 7b12e05..6f67f23 100644
--- a/authorization_test.go
+++ b/authorization_test.go
@@ -1,14 +1,15 @@
 package main
 
 import (
-	"./internal/api"
-	"./internal/helper"
-	"./internal/testhelper"
 	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"regexp"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 func okHandler(w http.ResponseWriter, _ *http.Request, _ *api.Response) {
diff --git a/cmd/gitlab-zip-cat/main.go b/cmd/gitlab-zip-cat/main.go
index e86edfd..29d233e 100644
--- a/cmd/gitlab-zip-cat/main.go
+++ b/cmd/gitlab-zip-cat/main.go
@@ -1,12 +1,13 @@
 package main
 
 import (
-	"../../internal/zipartifacts"
 	"archive/zip"
 	"flag"
 	"fmt"
 	"io"
 	"os"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
 )
 
 const progName = "gitlab-zip-cat"
@@ -25,7 +26,7 @@ func main() {
 	}
 
 	if len(os.Args) != 3 {
-		fmt.Fprintf(os.Stderr, "Usage: %s FILE.ZIP ENTRY", progName)
+		fmt.Fprintf(os.Stderr, "Usage: %s FILE.ZIP ENTRY\n", progName)
 		os.Exit(1)
 	}
 
diff --git a/cmd/gitlab-zip-metadata/main.go b/cmd/gitlab-zip-metadata/main.go
index b399275..1487701 100644
--- a/cmd/gitlab-zip-metadata/main.go
+++ b/cmd/gitlab-zip-metadata/main.go
@@ -1,10 +1,11 @@
 package main
 
 import (
-	"../../internal/zipartifacts"
 	"flag"
 	"fmt"
 	"os"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
 )
 
 const progName = "gitlab-zip-metadata"
diff --git a/internal/api/api.go b/internal/api/api.go
index b23ea7e..51c1ff2 100644
--- a/internal/api/api.go
+++ b/internal/api/api.go
@@ -1,8 +1,6 @@
 package api
 
 import (
-	"../badgateway"
-	"../helper"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -10,6 +8,9 @@ import (
 	"net/http"
 	"net/url"
 	"strings"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/badgateway"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
 type API struct {
@@ -38,15 +39,6 @@ type Response struct {
 	// 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
diff --git a/internal/artifacts/artifact_download.go b/internal/artifacts/artifact_download.go
index 71dd322..4c296bf 100644
--- a/internal/artifacts/artifact_download.go
+++ b/internal/artifacts/artifact_download.go
@@ -1,9 +1,6 @@
 package artifacts
 
 import (
-	"../api"
-	"../helper"
-	"../zipartifacts"
 	"bufio"
 	"errors"
 	"fmt"
@@ -15,6 +12,10 @@ import (
 	"path/filepath"
 	"strings"
 	"syscall"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
 )
 
 func detectFileContentType(fileName string) string {
diff --git a/internal/artifacts/artifact_download_test.go b/internal/artifacts/artifact_download_test.go
index c7a6cab..381affa 100644
--- a/internal/artifacts/artifact_download_test.go
+++ b/internal/artifacts/artifact_download_test.go
@@ -1,9 +1,6 @@
 package artifacts
 
 import (
-	"../api"
-	"../helper"
-	"../testhelper"
 	"archive/zip"
 	"encoding/base64"
 	"encoding/json"
@@ -13,6 +10,10 @@ import (
 	"net/http/httptest"
 	"os"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 func testArtifactDownloadServer(t *testing.T, archive string, entry string) *httptest.Server {
diff --git a/internal/artifacts/artifacts_upload.go b/internal/artifacts/artifacts_upload.go
index f060ec9..6bff536 100644
--- a/internal/artifacts/artifacts_upload.go
+++ b/internal/artifacts/artifacts_upload.go
@@ -1,10 +1,6 @@
 package artifacts
 
 import (
-	"../api"
-	"../helper"
-	"../upload"
-	"../zipartifacts"
 	"errors"
 	"fmt"
 	"io/ioutil"
@@ -13,6 +9,11 @@ import (
 	"os"
 	"os/exec"
 	"syscall"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
 )
 
 type artifactsUploadProcessor struct {
diff --git a/internal/artifacts/artifacts_upload_test.go b/internal/artifacts/artifacts_upload_test.go
index 63ab83f..c47b867 100644
--- a/internal/artifacts/artifacts_upload_test.go
+++ b/internal/artifacts/artifacts_upload_test.go
@@ -1,11 +1,6 @@
 package artifacts
 
 import (
-	"../api"
-	"../helper"
-	"../proxy"
-	"../testhelper"
-	"../zipartifacts"
 	"archive/zip"
 	"bytes"
 	"compress/gzip"
@@ -18,6 +13,12 @@ import (
 	"net/http/httptest"
 	"os"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/proxy"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
 )
 
 func testArtifactsUploadServer(t *testing.T, tempPath string) *httptest.Server {
diff --git a/internal/badgateway/roundtripper.go b/internal/badgateway/roundtripper.go
index b6c153c..5b7d855 100644
--- a/internal/badgateway/roundtripper.go
+++ b/internal/badgateway/roundtripper.go
@@ -1,13 +1,14 @@
 package badgateway
 
 import (
-	"../helper"
 	"bytes"
 	"fmt"
 	"io/ioutil"
 	"net"
 	"net/http"
 	"time"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
 // Values from http.DefaultTransport
diff --git a/internal/delay/responsewriter.go b/internal/delay/responsewriter.go
new file mode 100644
index 0000000..3493a5a
--- /dev/null
+++ b/internal/delay/responsewriter.go
@@ -0,0 +1,76 @@
+// Package delay exports delay.ResponseWriter. This type implements
+// http.ResponseWriter with the ability to delay setting the HTTP
+// response code (with WriteHeader()) until writing the first bufferSize
+// bytes. This makes it possible, up to a point, to 'change your mind'
+// about the HTTP status code. The caller must call
+// ResponseWriter.Flush() before returning from the handler (e.g. using
+// 'defer').
+
+package delay
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+)
+
+const bufferSize = 8192
+
+type ResponseWriter struct {
+	writer     http.ResponseWriter
+	status     int
+	bufWritten int
+	cap        int
+	flushed    bool
+	buffer     *bytes.Buffer
+}
+
+func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
+	return &ResponseWriter{
+		writer: w,
+		buffer: bytes.NewBuffer(make([]byte, 0, bufferSize)),
+		cap:    bufferSize,
+	}
+}
+
+func (rw *ResponseWriter) Write(buf []byte) (int, error) {
+	if !rw.flushed && len(buf)+rw.bufWritten <= rw.cap {
+		n, err := rw.buffer.Write(buf)
+		rw.bufWritten += n
+		return n, err
+	}
+
+	if err := rw.Flush(); err != nil {
+		return 0, err
+	}
+
+	return rw.writer.Write(buf)
+}
+
+func (rw *ResponseWriter) Header() http.Header {
+	return rw.writer.Header()
+}
+
+func (rw *ResponseWriter) WriteHeader(code int) {
+	if rw.status != 0 {
+		return
+	}
+	rw.status = code
+}
+
+func (rw *ResponseWriter) Flush() error {
+	if rw.flushed {
+		return nil
+	}
+	rw.flushed = true
+
+	if rw.status == 0 {
+		rw.writer.WriteHeader(http.StatusOK)
+	} else {
+		rw.writer.WriteHeader(rw.status)
+	}
+
+	_, err := io.Copy(rw.writer, rw.buffer)
+	rw.buffer = nil // "Release" the buffer for GC
+	return err
+}
diff --git a/internal/delay/responsewriter_test.go b/internal/delay/responsewriter_test.go
new file mode 100644
index 0000000..5717a2b
--- /dev/null
+++ b/internal/delay/responsewriter_test.go
@@ -0,0 +1,53 @@
+package delay
+
+import (
+	"fmt"
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+func TestSanity(t *testing.T) {
+	first, second := 200, 500
+	w := httptest.NewRecorder()
+	w.WriteHeader(first)
+	w.WriteHeader(second)
+	if code := w.Code; code != first {
+		t.Fatalf("Expected HTTP code %d, got %d", first, code)
+	}
+}
+
+func TestSmallResponse(t *testing.T) {
+	code := 500
+	body := "hello"
+	w := httptest.NewRecorder()
+	rw := NewResponseWriter(w)
+	fmt.Fprint(rw, body)
+	rw.WriteHeader(code)
+	rw.Flush()
+
+	if actualCode := w.Code; actualCode != code {
+		t.Fatalf("Expected code %d, got %d", code, actualCode)
+	}
+	if actualBody := w.Body.String(); actualBody != body {
+		t.Fatalf("Expected body %q, got %q", body, actualBody)
+	}
+}
+
+func TestLargeResponse(t *testing.T) {
+	code := 200
+	body := strings.Repeat("0123456789", bufferSize/5) // must exceed the buffer size
+	w := httptest.NewRecorder()
+	rw := NewResponseWriter(w)
+	fmt.Fprint(rw, body)
+	// Because the 'body' was too long this 500 should be ignored
+	rw.WriteHeader(500)
+	rw.Flush()
+
+	if actualCode := w.Code; actualCode != code {
+		t.Fatalf("Expected code %d, got %d", code, actualCode)
+	}
+	if actualBody := w.Body.String(); actualBody != body {
+		t.Fatalf("Expected body %q, got %q", body, actualBody)
+	}
+}
diff --git a/internal/git/archive.go b/internal/git/archive.go
index 854c887..cffc12c 100644
--- a/internal/git/archive.go
+++ b/internal/git/archive.go
@@ -5,8 +5,6 @@ In this file we handle 'git archive' downloads
 package git
 
 import (
-	"../api"
-	"../helper"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -18,13 +16,28 @@ import (
 	"path/filepath"
 	"syscall"
 	"time"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
 )
 
-func GetArchive(a *api.API) http.Handler {
-	return repoPreAuthorizeHandler(a, handleGetArchive)
+type archive struct{ senddata.Prefix }
+type archiveParams struct {
+	RepoPath      string
+	ArchivePath   string
+	ArchivePrefix string
+	CommitId      string
 }
 
-func handleGetArchive(w http.ResponseWriter, r *http.Request, a *api.Response) {
+var SendArchive = &archive{"git-archive:"}
+
+func (a *archive) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+	var params archiveParams
+	if err := a.Unpack(&params, sendData); err != nil {
+		helper.Fail500(w, fmt.Errorf("SendArchive: unpack sendData: %v", err))
+		return
+	}
+
 	var format string
 	urlPath := r.URL.Path
 	switch filepath.Base(urlPath) {
@@ -41,11 +54,11 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *api.Response) {
 		return
 	}
 
-	archiveFilename := path.Base(a.ArchivePath)
+	archiveFilename := path.Base(params.ArchivePath)
 
-	if cachedArchive, err := os.Open(a.ArchivePath); err == nil {
+	if cachedArchive, err := os.Open(params.ArchivePath); err == nil {
 		defer cachedArchive.Close()
-		log.Printf("Serving cached file %q", a.ArchivePath)
+		log.Printf("Serving cached file %q", params.ArchivePath)
 		setArchiveHeaders(w, format, archiveFilename)
 		// Even if somebody deleted the cachedArchive from disk since we opened
 		// the file, Unix file semantics guarantee we can still read from the
@@ -58,7 +71,7 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *api.Response) {
 	// safe. We create the tempfile in the same directory as the final cached
 	// archive we want to create so that we can use an atomic link(2) operation
 	// to finalize the cached archive.
-	tempFile, err := prepareArchiveTempfile(path.Dir(a.ArchivePath), archiveFilename)
+	tempFile, err := prepareArchiveTempfile(path.Dir(params.ArchivePath), archiveFilename)
 	if err != nil {
 		helper.Fail500(w, fmt.Errorf("handleGetArchive: create tempfile: %v", err))
 		return
@@ -68,7 +81,7 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *api.Response) {
 
 	compressCmd, archiveFormat := parseArchiveFormat(format)
 
-	archiveCmd := gitCommand("", "git", "--git-dir="+a.RepoPath, "archive", "--format="+archiveFormat, "--prefix="+a.ArchivePrefix+"/", a.CommitId)
+	archiveCmd := gitCommand("", "git", "--git-dir="+params.RepoPath, "archive", "--format="+archiveFormat, "--prefix="+params.ArchivePrefix+"/", params.CommitId)
 	archiveStdout, err := archiveCmd.StdoutPipe()
 	if err != nil {
 		helper.Fail500(w, fmt.Errorf("handleGetArchive: archive stdout: %v", err))
@@ -125,13 +138,14 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *api.Response) {
 		}
 	}
 
-	if err := finalizeCachedArchive(tempFile, a.ArchivePath); err != nil {
+	if err := finalizeCachedArchive(tempFile, params.ArchivePath); err != nil {
 		helper.LogError(fmt.Errorf("handleGetArchive: finalize cached archive: %v", err))
 		return
 	}
 }
 
 func setArchiveHeaders(w http.ResponseWriter, format string, archiveFilename string) {
+	w.Header().Del("Content-Length")
 	w.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, archiveFilename))
 	if format == "zip" {
 		w.Header().Add("Content-Type", "application/zip")
diff --git a/internal/git/blob.go b/internal/git/blob.go
index aa1b7d6..9f4634d 100644
--- a/internal/git/blob.go
+++ b/internal/git/blob.go
@@ -1,35 +1,40 @@
 package git
 
 import (
-	"../helper"
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
 	"io"
 	"log"
 	"net/http"
 	"strings"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
 )
 
-type blobParams struct {
-	RepoPath string
-	BlobId   string
-}
+type blob struct{ senddata.Prefix }
+type blobParams struct{ RepoPath, BlobId string }
 
-const SendBlobPrefix = "git-blob:"
+var SendBlob = &blob{"git-blob:"}
 
-func SendBlob(w http.ResponseWriter, r *http.Request, sendData string) {
-	params, err := unpackSendData(sendData)
-	if err != nil {
+func (b *blob) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+	var params blobParams
+	if err := b.Unpack(&params, sendData); err != nil {
 		helper.Fail500(w, fmt.Errorf("SendBlob: unpack sendData: %v", err))
 		return
 	}
+
 	log.Printf("SendBlob: sending %q for %q", params.BlobId, r.URL.Path)
 
+	sizeOutput, err := gitCommand("", "git", "--git-dir="+params.RepoPath, "cat-file", "-s", params.BlobId).Output()
+	if err != nil {
+		helper.Fail500(w, fmt.Errorf("SendBlob: get blob size: %v", err))
+		return
+	}
+
 	gitShowCmd := gitCommand("", "git", "--git-dir="+params.RepoPath, "cat-file", "blob", params.BlobId)
 	stdout, err := gitShowCmd.StdoutPipe()
 	if err != nil {
-		helper.Fail500(w, fmt.Errorf("SendBlob: git  stdout: %v", err))
+		helper.Fail500(w, fmt.Errorf("SendBlob: git cat-file stdout: %v", err))
 		return
 	}
 	if err := gitShowCmd.Start(); err != nil {
@@ -38,6 +43,7 @@ func SendBlob(w http.ResponseWriter, r *http.Request, sendData string) {
 	}
 	defer helper.CleanUpProcessGroup(gitShowCmd)
 
+	w.Header().Set("Content-Length", strings.TrimSpace(string(sizeOutput)))
 	if _, err := io.Copy(w, stdout); err != nil {
 		helper.LogError(fmt.Errorf("SendBlob: copy git cat-file stdout: %v", err))
 		return
@@ -47,15 +53,3 @@ func SendBlob(w http.ResponseWriter, r *http.Request, sendData string) {
 		return
 	}
 }
-
-func unpackSendData(sendData string) (*blobParams, error) {
-	jsonBytes, err := base64.URLEncoding.DecodeString(strings.TrimPrefix(sendData, SendBlobPrefix))
-	if err != nil {
-		return nil, err
-	}
-	result := &blobParams{}
-	if err := json.Unmarshal([]byte(jsonBytes), result); err != nil {
-		return nil, err
-	}
-	return result, nil
-}
diff --git a/internal/git/git-http.go b/internal/git/git-http.go
index d0c41b8..4b75e04 100644
--- a/internal/git/git-http.go
+++ b/internal/git/git-http.go
@@ -5,8 +5,6 @@ In this file we handle the Git 'smart HTTP' protocol
 package git
 
 import (
-	"../api"
-	"../helper"
 	"errors"
 	"fmt"
 	"io"
@@ -16,6 +14,10 @@ import (
 	"path"
 	"path/filepath"
 	"strings"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/delay"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
 func GetInfoRefs(a *api.API) http.Handler {
@@ -52,7 +54,10 @@ func repoPreAuthorizeHandler(myAPI *api.API, handleFunc api.HandleFunc) http.Han
 	}, "")
 }
 
-func handleGetInfoRefs(w http.ResponseWriter, r *http.Request, a *api.Response) {
+func handleGetInfoRefs(_w http.ResponseWriter, r *http.Request, a *api.Response) {
+	w := delay.NewResponseWriter(_w)
+	defer w.Flush()
+
 	rpc := r.URL.Query().Get("service")
 	if !(rpc == "git-upload-pack" || rpc == "git-receive-pack") {
 		// The 'dumb' Git HTTP protocol is not supported
@@ -77,26 +82,28 @@ func handleGetInfoRefs(w http.ResponseWriter, r *http.Request, a *api.Response)
 	// Start writing the response
 	w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
 	w.Header().Add("Cache-Control", "no-cache")
-	w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just return
 	if err := pktLine(w, fmt.Sprintf("# service=%s\n", rpc)); err != nil {
-		helper.LogError(fmt.Errorf("handleGetInfoRefs: pktLine: %v", err))
+		helper.Fail500(w, fmt.Errorf("handleGetInfoRefs: pktLine: %v", err))
 		return
 	}
 	if err := pktFlush(w); err != nil {
-		helper.LogError(fmt.Errorf("handleGetInfoRefs: pktFlush: %v", err))
+		helper.Fail500(w, fmt.Errorf("handleGetInfoRefs: pktFlush: %v", err))
 		return
 	}
 	if _, err := io.Copy(w, stdout); err != nil {
-		helper.LogError(fmt.Errorf("handleGetInfoRefs: copy output of %v: %v", cmd.Args, err))
+		helper.Fail500(w, fmt.Errorf("handleGetInfoRefs: copy output of %v: %v", cmd.Args, err))
 		return
 	}
 	if err := cmd.Wait(); err != nil {
-		helper.LogError(fmt.Errorf("handleGetInfoRefs: wait for %v: %v", cmd.Args, err))
+		helper.Fail500(w, fmt.Errorf("handleGetInfoRefs: wait for %v: %v", cmd.Args, err))
 		return
 	}
 }
 
-func handlePostRPC(w http.ResponseWriter, r *http.Request, a *api.Response) {
+func handlePostRPC(_w http.ResponseWriter, r *http.Request, a *api.Response) {
+	w := delay.NewResponseWriter(_w)
+	defer w.Flush()
+
 	var err error
 
 	// Get Git action from URL
@@ -142,15 +149,14 @@ func handlePostRPC(w http.ResponseWriter, r *http.Request, a *api.Response) {
 	// Start writing the response
 	w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-result", action))
 	w.Header().Add("Cache-Control", "no-cache")
-	w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just return
 
 	// This io.Copy may take a long time, both for Git push and pull.
 	if _, err := io.Copy(w, stdout); err != nil {
-		helper.LogError(fmt.Errorf("handlePostRPC copy output of %v: %v", cmd.Args, err))
+		helper.Fail500(w, fmt.Errorf("handlePostRPC copy output of %v: %v", cmd.Args, err))
 		return
 	}
 	if err := cmd.Wait(); err != nil {
-		helper.LogError(fmt.Errorf("handlePostRPC wait for %v: %v", cmd.Args, err))
+		helper.Fail500(w, fmt.Errorf("handlePostRPC wait for %v: %v", cmd.Args, err))
 		return
 	}
 }
diff --git a/internal/lfs/lfs.go b/internal/lfs/lfs.go
index 6d78cb6..c65256b 100644
--- a/internal/lfs/lfs.go
+++ b/internal/lfs/lfs.go
@@ -5,9 +5,6 @@ In this file we handle git lfs objects downloads and uploads
 package lfs
 
 import (
-	"../api"
-	"../helper"
-	"../proxy"
 	"bytes"
 	"crypto/sha256"
 	"encoding/hex"
@@ -18,10 +15,13 @@ import (
 	"net/http"
 	"os"
 	"path/filepath"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
-func PutStore(a *api.API, p *proxy.Proxy) http.Handler {
-	return lfsAuthorizeHandler(a, handleStoreLfsObject(p))
+func PutStore(a *api.API, h http.Handler) http.Handler {
+	return lfsAuthorizeHandler(a, handleStoreLfsObject(h))
 }
 
 func lfsAuthorizeHandler(myAPI *api.API, handleFunc api.HandleFunc) http.Handler {
diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go
index 39422f9..f83d41d 100644
--- a/internal/proxy/proxy.go
+++ b/internal/proxy/proxy.go
@@ -1,12 +1,12 @@
 package proxy
 
 import (
-	"../badgateway"
-	"../helper"
-	"../senddata"
 	"net/http"
 	"net/http/httputil"
 	"net/url"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/badgateway"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
 type Proxy struct {
@@ -34,8 +34,6 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 	// Set Workhorse version
 	req.Header.Set("Gitlab-Workhorse", p.Version)
-	rw := senddata.NewSendFileResponseWriter(w, &req)
-	defer rw.Flush()
 
-	p.reverseProxy.ServeHTTP(&rw, &req)
+	p.reverseProxy.ServeHTTP(w, &req)
 }
diff --git a/internal/senddata/injecter.go b/internal/senddata/injecter.go
new file mode 100644
index 0000000..72d8039
--- /dev/null
+++ b/internal/senddata/injecter.go
@@ -0,0 +1,32 @@
+package senddata
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"net/http"
+	"strings"
+)
+
+type Injecter interface {
+	Match(string) bool
+	Inject(http.ResponseWriter, *http.Request, string)
+}
+
+type Prefix string
+
+const HeaderKey = "Gitlab-Workhorse-Send-Data"
+
+func (p Prefix) Match(s string) bool {
+	return strings.HasPrefix(s, string(p))
+}
+
+func (p Prefix) Unpack(result interface{}, sendData string) error {
+	jsonBytes, err := base64.URLEncoding.DecodeString(strings.TrimPrefix(sendData, string(p)))
+	if err != nil {
+		return err
+	}
+	if err := json.Unmarshal([]byte(jsonBytes), result); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/internal/senddata/senddata.go b/internal/senddata/senddata.go
new file mode 100644
index 0000000..cc163c9
--- /dev/null
+++ b/internal/senddata/senddata.go
@@ -0,0 +1,75 @@
+package senddata
+
+import (
+	"net/http"
+)
+
+type sendDataResponseWriter struct {
+	rw        http.ResponseWriter
+	status    int
+	hijacked  bool
+	req       *http.Request
+	injecters []Injecter
+}
+
+func SendData(h http.Handler, injecters ...Injecter) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		s := sendDataResponseWriter{
+			rw:        w,
+			req:       r,
+			injecters: injecters,
+		}
+		defer s.Flush()
+		h.ServeHTTP(&s, r)
+	})
+}
+
+func (s *sendDataResponseWriter) Header() http.Header {
+	return s.rw.Header()
+}
+
+func (s *sendDataResponseWriter) Write(data []byte) (n int, err error) {
+	if s.status == 0 {
+		s.WriteHeader(http.StatusOK)
+	}
+	if s.hijacked {
+		return
+	}
+	return s.rw.Write(data)
+}
+
+func (s *sendDataResponseWriter) WriteHeader(status int) {
+	if s.status != 0 {
+		return
+	}
+	s.status = status
+
+	if s.status == http.StatusOK && s.tryInject() {
+		return
+	}
+
+	s.Header().Del(HeaderKey)
+	s.rw.WriteHeader(s.status)
+}
+
+func (s *sendDataResponseWriter) tryInject() bool {
+	header := s.Header().Get(HeaderKey)
+	s.Header().Del(HeaderKey)
+	if header == "" {
+		return false
+	}
+
+	for _, injecter := range s.injecters {
+		if injecter.Match(header) {
+			s.hijacked = true
+			injecter.Inject(s.rw, s.req, header)
+			return true
+		}
+	}
+
+	return false
+}
+
+func (s *sendDataResponseWriter) Flush() {
+	s.WriteHeader(http.StatusOK)
+}
diff --git a/internal/senddata/senddata_test.go b/internal/senddata/senddata_test.go
new file mode 100644
index 0000000..f28f3f4
--- /dev/null
+++ b/internal/senddata/senddata_test.go
@@ -0,0 +1,20 @@
+package senddata
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func TestHeaderDelete(t *testing.T) {
+	for _, code := range []int{200, 400} {
+		recorder := httptest.NewRecorder()
+		rw := &sendDataResponseWriter{rw: recorder, req: &http.Request{}}
+		rw.Header().Set(HeaderKey, "foobar")
+		rw.WriteHeader(code)
+
+		if header := recorder.Header().Get(HeaderKey); header != "" {
+			t.Fatalf("HTTP %d response: expected header to be empty, found %q", code, header)
+		}
+	}
+}
diff --git a/internal/senddata/sendfile.go b/internal/sendfile/sendfile.go
similarity index 69%
rename from internal/senddata/sendfile.go
rename to internal/sendfile/sendfile.go
index 7d851d6..cd28e45 100644
--- a/internal/senddata/sendfile.go
+++ b/internal/sendfile/sendfile.go
@@ -4,21 +4,17 @@ via the X-Sendfile mechanism. All that is needed in the Rails code is the
 'send_file' method.
 */
 
-package senddata
+package sendfile
 
 import (
-	"../git"
-	"../helper"
 	"log"
 	"net/http"
-	"strings"
-)
 
-const (
-	sendDataResponseHeader = "Gitlab-Workhorse-Send-Data"
-	sendFileResponseHeader = "X-Sendfile"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
+const sendFileResponseHeader = "X-Sendfile"
+
 type sendFileResponseWriter struct {
 	rw       http.ResponseWriter
 	status   int
@@ -26,14 +22,17 @@ type sendFileResponseWriter struct {
 	req      *http.Request
 }
 
-func NewSendFileResponseWriter(rw http.ResponseWriter, req *http.Request) sendFileResponseWriter {
-	s := sendFileResponseWriter{
-		rw:  rw,
-		req: req,
-	}
-	// Advertise to upstream (Rails) that we support X-Sendfile
-	req.Header.Set("X-Sendfile-Type", "X-Sendfile")
-	return s
+func SendFile(h http.Handler) http.Handler {
+	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		s := &sendFileResponseWriter{
+			rw:  rw,
+			req: req,
+		}
+		// Advertise to upstream (Rails) that we support X-Sendfile
+		req.Header.Set("X-Sendfile-Type", "X-Sendfile")
+		defer s.Flush()
+		h.ServeHTTP(s, req)
+	})
 }
 
 func (s *sendFileResponseWriter) Header() http.Header {
@@ -70,12 +69,6 @@ func (s *sendFileResponseWriter) WriteHeader(status int) {
 		sendFileFromDisk(s.rw, s.req, file)
 		return
 	}
-	if sendData := s.Header().Get(sendDataResponseHeader); strings.HasPrefix(sendData, git.SendBlobPrefix) {
-		s.Header().Del(sendDataResponseHeader)
-		s.hijacked = true
-		git.SendBlob(s.rw, s.req, sendData)
-		return
-	}
 
 	s.rw.WriteHeader(s.status)
 	return
diff --git a/internal/staticpages/deploy_page.go b/internal/staticpages/deploy_page.go
index b777fbc..d08ed44 100644
--- a/internal/staticpages/deploy_page.go
+++ b/internal/staticpages/deploy_page.go
@@ -1,10 +1,11 @@
 package staticpages
 
 import (
-	"../helper"
 	"io/ioutil"
 	"net/http"
 	"path/filepath"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
 func (s *Static) DeployPage(handler http.Handler) http.Handler {
diff --git a/internal/staticpages/deploy_page_test.go b/internal/staticpages/deploy_page_test.go
index 2e4be67..fd02de4 100644
--- a/internal/staticpages/deploy_page_test.go
+++ b/internal/staticpages/deploy_page_test.go
@@ -1,13 +1,14 @@
 package staticpages
 
 import (
-	"../testhelper"
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
 	"os"
 	"path/filepath"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 func TestIfNoDeployPageExist(t *testing.T) {
diff --git a/internal/staticpages/error_pages.go b/internal/staticpages/error_pages.go
index 958c32e..1f57636 100644
--- a/internal/staticpages/error_pages.go
+++ b/internal/staticpages/error_pages.go
@@ -1,12 +1,13 @@
 package staticpages
 
 import (
-	"../helper"
 	"fmt"
 	"io/ioutil"
 	"log"
 	"net/http"
 	"path/filepath"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
 type errorPageResponseWriter struct {
diff --git a/internal/staticpages/error_pages_test.go b/internal/staticpages/error_pages_test.go
index 72d259d..c391c3b 100644
--- a/internal/staticpages/error_pages_test.go
+++ b/internal/staticpages/error_pages_test.go
@@ -1,7 +1,6 @@
 package staticpages
 
 import (
-	"../testhelper"
 	"fmt"
 	"io/ioutil"
 	"net/http"
@@ -9,6 +8,8 @@ import (
 	"os"
 	"path/filepath"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 func TestIfErrorPageIsPresented(t *testing.T) {
diff --git a/internal/staticpages/servefile.go b/internal/staticpages/servefile.go
index 5349935..82e8d1d 100644
--- a/internal/staticpages/servefile.go
+++ b/internal/staticpages/servefile.go
@@ -1,14 +1,15 @@
 package staticpages
 
 import (
-	"../helper"
-	"../urlprefix"
 	"log"
 	"net/http"
 	"os"
 	"path/filepath"
 	"strings"
 	"time"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/urlprefix"
 )
 
 type CacheMode int
diff --git a/internal/staticpages/servefile_test.go b/internal/staticpages/servefile_test.go
index 860d0a8..652573d 100644
--- a/internal/staticpages/servefile_test.go
+++ b/internal/staticpages/servefile_test.go
@@ -1,7 +1,6 @@
 package staticpages
 
 import (
-	"../testhelper"
 	"bytes"
 	"compress/gzip"
 	"io/ioutil"
@@ -10,6 +9,8 @@ import (
 	"os"
 	"path/filepath"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 func TestServingNonExistingFile(t *testing.T) {
diff --git a/internal/upload/uploads.go b/internal/upload/uploads.go
index 55dab71..2318b9e 100644
--- a/internal/upload/uploads.go
+++ b/internal/upload/uploads.go
@@ -1,7 +1,6 @@
 package upload
 
 import (
-	"../helper"
 	"bytes"
 	"fmt"
 	"io"
@@ -9,6 +8,8 @@ import (
 	"mime/multipart"
 	"net/http"
 	"os"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
 type MultipartFormProcessor interface {
diff --git a/internal/upload/uploads_test.go b/internal/upload/uploads_test.go
index f779b50..0f8272f 100644
--- a/internal/upload/uploads_test.go
+++ b/internal/upload/uploads_test.go
@@ -1,9 +1,6 @@
 package upload
 
 import (
-	"../helper"
-	"../proxy"
-	"../testhelper"
 	"bytes"
 	"errors"
 	"fmt"
@@ -16,6 +13,10 @@ import (
 	"regexp"
 	"strings"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/proxy"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 var nilHandler = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
diff --git a/internal/upstream/development_test.go b/internal/upstream/development_test.go
index d83fd35..bfe6a4a 100644
--- a/internal/upstream/development_test.go
+++ b/internal/upstream/development_test.go
@@ -1,10 +1,11 @@
 package upstream
 
 import (
-	"../testhelper"
 	"net/http"
 	"net/http/httptest"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 func TestDevelopmentModeEnabled(t *testing.T) {
diff --git a/internal/upstream/handlers.go b/internal/upstream/handlers.go
index d1a96ce..7a7628c 100644
--- a/internal/upstream/handlers.go
+++ b/internal/upstream/handlers.go
@@ -1,11 +1,12 @@
 package upstream
 
 import (
-	"../helper"
 	"compress/gzip"
 	"fmt"
 	"io"
 	"net/http"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
 )
 
 func contentEncodingHandler(h http.Handler) http.Handler {
diff --git a/internal/upstream/handlers_test.go b/internal/upstream/handlers_test.go
index b5357ad..3c53621 100644
--- a/internal/upstream/handlers_test.go
+++ b/internal/upstream/handlers_test.go
@@ -1,7 +1,6 @@
 package upstream
 
 import (
-	"../testhelper"
 	"bytes"
 	"compress/gzip"
 	"fmt"
@@ -10,6 +9,8 @@ import (
 	"net/http/httptest"
 	"reflect"
 	"testing"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 func TestGzipEncoding(t *testing.T) {
diff --git a/internal/upstream/routes.go b/internal/upstream/routes.go
index 0fe09d4..d73fccc 100644
--- a/internal/upstream/routes.go
+++ b/internal/upstream/routes.go
@@ -1,14 +1,17 @@
 package upstream
 
 import (
-	apipkg "../api"
-	"../artifacts"
-	"../git"
-	"../lfs"
-	proxypkg "../proxy"
-	"../staticpages"
 	"net/http"
 	"regexp"
+
+	apipkg "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/artifacts"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/git"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/lfs"
+	proxypkg "gitlab.com/gitlab-org/gitlab-workhorse/internal/proxy"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/sendfile"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/staticpages"
 )
 
 type route struct {
@@ -37,10 +40,14 @@ func (u *Upstream) configureRoutes() {
 		u.RoundTripper,
 	)
 	static := &staticpages.Static{u.DocumentRoot}
-	proxy := proxypkg.NewProxy(
-		u.Backend,
-		u.Version,
-		u.RoundTripper,
+	proxy := senddata.SendData(
+		sendfile.SendFile(proxypkg.NewProxy(
+			u.Backend,
+			u.Version,
+			u.RoundTripper,
+		)),
+		git.SendArchive,
+		git.SendBlob,
 	)
 
 	u.Routes = []route{
@@ -50,20 +57,6 @@ func (u *Upstream) configureRoutes() {
 		route{"POST", regexp.MustCompile(gitProjectPattern + `git-receive-pack\z`), contentEncodingHandler(git.PostRPC(api))},
 		route{"PUT", regexp.MustCompile(gitProjectPattern + `gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`), lfs.PutStore(api, proxy)},
 
-		// Repository Archive
-		route{"GET", regexp.MustCompile(projectPattern + `repository/archive\z`), git.GetArchive(api)},
-		route{"GET", regexp.MustCompile(projectPattern + `repository/archive.zip\z`), git.GetArchive(api)},
-		route{"GET", regexp.MustCompile(projectPattern + `repository/archive.tar\z`), git.GetArchive(api)},
-		route{"GET", regexp.MustCompile(projectPattern + `repository/archive.tar.gz\z`), git.GetArchive(api)},
-		route{"GET", regexp.MustCompile(projectPattern + `repository/archive.tar.bz2\z`), git.GetArchive(api)},
-
-		// Repository Archive API
-		route{"GET", regexp.MustCompile(projectsAPIPattern + `repository/archive\z`), git.GetArchive(api)},
-		route{"GET", regexp.MustCompile(projectsAPIPattern + `repository/archive.zip\z`), git.GetArchive(api)},
-		route{"GET", regexp.MustCompile(projectsAPIPattern + `repository/archive.tar\z`), git.GetArchive(api)},
-		route{"GET", regexp.MustCompile(projectsAPIPattern + `repository/archive.tar.gz\z`), git.GetArchive(api)},
-		route{"GET", regexp.MustCompile(projectsAPIPattern + `repository/archive.tar.bz2\z`), git.GetArchive(api)},
-
 		// CI Artifacts
 		route{"GET", regexp.MustCompile(projectPattern + `builds/[0-9]+/artifacts/file/`), contentEncodingHandler(artifacts.DownloadArtifact(api))},
 		route{"POST", regexp.MustCompile(ciAPIPattern + `v1/builds/[0-9]+/artifacts\z`), contentEncodingHandler(artifacts.UploadArtifacts(api, proxy))},
diff --git a/internal/upstream/upstream.go b/internal/upstream/upstream.go
index 19dda8d..465fd6f 100644
--- a/internal/upstream/upstream.go
+++ b/internal/upstream/upstream.go
@@ -7,14 +7,15 @@ In this file we handle request routing and interaction with the authBackend.
 package upstream
 
 import (
-	"../badgateway"
-	"../helper"
-	"../urlprefix"
 	"fmt"
 	"net/http"
 	"net/url"
 	"strings"
 	"time"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/badgateway"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/urlprefix"
 )
 
 var DefaultBackend = helper.URLMustParse("http://localhost:8080")
diff --git a/main.go b/main.go
index 91312ae..16650e5 100644
--- a/main.go
+++ b/main.go
@@ -14,7 +14,6 @@ In this file we start the web server and hand off to the upstream type.
 package main
 
 import (
-	"./internal/upstream"
 	"flag"
 	"fmt"
 	"log"
@@ -24,6 +23,8 @@ import (
 	"os"
 	"syscall"
 	"time"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream"
 )
 
 // Current version of GitLab Workhorse
@@ -37,7 +38,7 @@ var authBackend = URLFlag("authBackend", upstream.DefaultBackend, "Authenticatio
 var authSocket = flag.String("authSocket", "", "Optional: Unix domain socket to dial authBackend at")
 var pprofListenAddr = flag.String("pprofListenAddr", "", "pprof listening address, e.g. 'localhost:6060'")
 var documentRoot = flag.String("documentRoot", "public", "Path to static files content")
-var proxyHeadersTimeout = flag.Duration("proxyHeadersTimeout", time.Minute, "How long to wait for response headers when proxying the request")
+var proxyHeadersTimeout = flag.Duration("proxyHeadersTimeout", 5*time.Minute, "How long to wait for response headers when proxying the request")
 var developmentMode = flag.Bool("developmentMode", false, "Allow to serve assets from Rails app")
 
 func main() {
diff --git a/main_test.go b/main_test.go
index a4fed1c..9ce37f7 100644
--- a/main_test.go
+++ b/main_test.go
@@ -1,10 +1,6 @@
 package main
 
 import (
-	"./internal/api"
-	"./internal/helper"
-	"./internal/testhelper"
-	"./internal/upstream"
 	"bytes"
 	"encoding/base64"
 	"encoding/json"
@@ -22,6 +18,11 @@ import (
 	"strings"
 	"testing"
 	"time"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream"
 )
 
 const scratchDir = "testdata/scratch"
@@ -115,7 +116,7 @@ func TestAllowedDownloadZip(t *testing.T) {
 
 	// Prepare test server and backend
 	archiveName := "foobar.zip"
-	ts := testAuthServer(nil, 200, archiveOkBody(t, archiveName))
+	ts := archiveOKServer(t, archiveName)
 	defer ts.Close()
 	ws := startWorkhorseServer(ts.URL)
 	defer ws.Close()
@@ -134,7 +135,7 @@ func TestAllowedDownloadTar(t *testing.T) {
 
 	// Prepare test server and backend
 	archiveName := "foobar.tar"
-	ts := testAuthServer(nil, 200, archiveOkBody(t, archiveName))
+	ts := archiveOKServer(t, archiveName)
 	defer ts.Close()
 	ws := startWorkhorseServer(ts.URL)
 	defer ws.Close()
@@ -153,7 +154,7 @@ func TestAllowedDownloadTarGz(t *testing.T) {
 
 	// Prepare test server and backend
 	archiveName := "foobar.tar.gz"
-	ts := testAuthServer(nil, 200, archiveOkBody(t, archiveName))
+	ts := archiveOKServer(t, archiveName)
 	defer ts.Close()
 	ws := startWorkhorseServer(ts.URL)
 	defer ws.Close()
@@ -172,7 +173,7 @@ func TestAllowedDownloadTarBz2(t *testing.T) {
 
 	// Prepare test server and backend
 	archiveName := "foobar.tar.bz2"
-	ts := testAuthServer(nil, 200, archiveOkBody(t, archiveName))
+	ts := archiveOKServer(t, archiveName)
 	defer ts.Close()
 	ws := startWorkhorseServer(ts.URL)
 	defer ws.Close()
@@ -191,7 +192,7 @@ func TestAllowedApiDownloadZip(t *testing.T) {
 
 	// Prepare test server and backend
 	archiveName := "foobar.zip"
-	ts := testAuthServer(nil, 200, archiveOkBody(t, archiveName))
+	ts := archiveOKServer(t, archiveName)
 	defer ts.Close()
 	ws := startWorkhorseServer(ts.URL)
 	defer ws.Close()
@@ -210,7 +211,7 @@ func TestAllowedApiDownloadZipWithSlash(t *testing.T) {
 
 	// Prepare test server and backend
 	archiveName := "foobar.zip"
-	ts := testAuthServer(nil, 200, archiveOkBody(t, archiveName))
+	ts := archiveOKServer(t, archiveName)
 	defer ts.Close()
 	ws := startWorkhorseServer(ts.URL)
 	defer ws.Close()
@@ -233,7 +234,7 @@ func TestDownloadCacheHit(t *testing.T) {
 
 	// Prepare test server and backend
 	archiveName := "foobar.zip"
-	ts := testAuthServer(nil, 200, archiveOkBody(t, archiveName))
+	ts := archiveOKServer(t, archiveName)
 	defer ts.Close()
 	ws := startWorkhorseServer(ts.URL)
 	defer ws.Close()
@@ -264,7 +265,7 @@ func TestDownloadCacheCreate(t *testing.T) {
 
 	// Prepare test server and backend
 	archiveName := "foobar.zip"
-	ts := testAuthServer(nil, 200, archiveOkBody(t, archiveName))
+	ts := archiveOKServer(t, archiveName)
 	defer ts.Close()
 	ws := startWorkhorseServer(ts.URL)
 	defer ws.Close()
@@ -563,8 +564,6 @@ func TestGetGitBlob(t *testing.T) {
 		responseJSON := fmt.Sprintf(`{"RepoPath":"%s","BlobId":"%s"}`, path.Join(testRepoRoot, testRepo), blobId)
 		encodedJSON := base64.StdEncoding.EncodeToString([]byte(responseJSON))
 		w.Header().Set(headerKey, "git-blob:"+encodedJSON)
-		// Prevent the Go HTTP server from setting the Content-Length to 0.
-		w.Header().Set("Transfer-Encoding", "chunked")
 		if _, err := fmt.Fprintf(w, "GNU General Public License"); err != nil {
 			t.Fatal(err)
 		}
@@ -592,6 +591,9 @@ func TestGetGitBlob(t *testing.T) {
 	if len(body) != blobLength {
 		t.Fatalf("Expected body of %d bytes, got %d", blobLength, len(body))
 	}
+	if cl := resp.Header.Get("Content-Length"); cl != fmt.Sprintf("%d", blobLength) {
+		t.Fatalf("Expected Content-Length %v, got %q", blobLength, cl)
+	}
 	if !strings.HasPrefix(string(body), "The MIT License (MIT)") {
 		t.Fatalf("Expected MIT license, got %q", body)
 	}
@@ -659,6 +661,29 @@ func testAuthServer(url *regexp.Regexp, code int, body interface{}) *httptest.Se
 	})
 }
 
+func archiveOKServer(t *testing.T, archiveName string) *httptest.Server {
+	return testhelper.TestServerWithHandler(regexp.MustCompile("."), func(w http.ResponseWriter, r *http.Request) {
+		cwd, err := os.Getwd()
+		if err != nil {
+			t.Fatal(err)
+		}
+		archivePath := path.Join(cwd, cacheDir, archiveName)
+
+		params := struct{ RepoPath, ArchivePath, CommitId, ArchivePrefix string }{
+			repoPath(t),
+			archivePath,
+			"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd",
+			"foobar123",
+		}
+		jsonData, err := json.Marshal(params)
+		if err != nil {
+			t.Fatal(err)
+		}
+		encodedJSON := base64.StdEncoding.EncodeToString(jsonData)
+		w.Header().Set("Gitlab-Workhorse-Send-Data", "git-archive:"+encodedJSON)
+	})
+}
+
 func startWorkhorseServer(authBackend string) *httptest.Server {
 	u := upstream.NewUpstream(
 		helper.URLMustParse(authBackend),
@@ -686,21 +711,6 @@ func gitOkBody(t *testing.T) interface{} {
 	}
 }
 
-func archiveOkBody(t *testing.T, archiveName string) interface{} {
-	cwd, err := os.Getwd()
-	if err != nil {
-		t.Fatal(err)
-	}
-	archivePath := path.Join(cwd, cacheDir, archiveName)
-
-	return &api.Response{
-		RepoPath:      repoPath(t),
-		ArchivePath:   archivePath,
-		CommitId:      "c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd",
-		ArchivePrefix: "foobar123",
-	}
-}
-
 func repoPath(t *testing.T) string {
 	cwd, err := os.Getwd()
 	if err != nil {
diff --git a/proxy_test.go b/proxy_test.go
index 079e7ee..917571e 100644
--- a/proxy_test.go
+++ b/proxy_test.go
@@ -1,10 +1,6 @@
 package main
 
 import (
-	"./internal/badgateway"
-	"./internal/helper"
-	"./internal/proxy"
-	"./internal/testhelper"
 	"bytes"
 	"fmt"
 	"io"
@@ -14,6 +10,11 @@ import (
 	"regexp"
 	"testing"
 	"time"
+
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/badgateway"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/proxy"
+	"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
 )
 
 func newProxy(url string, rt *badgateway.RoundTripper) *proxy.Proxy {

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/gitlab-workhorse.git



More information about the Pkg-ruby-extras-commits mailing list