diff --git a/archive.go b/archive.go
index 76a78f7000e419c9d877d0cbda373dabe6754919..a91b2b7a1dea291e834c58ccbf051753e1d6a124 100644
--- a/archive.go
+++ b/archive.go
@@ -5,6 +5,7 @@ In this file we handle 'git archive' downloads
 package main
 
 import (
+	"./internal/api"
 	"./internal/helper"
 	"fmt"
 	"io"
@@ -18,7 +19,7 @@ import (
 	"time"
 )
 
-func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
+func handleGetArchive(w http.ResponseWriter, r *http.Request, a *api.Response) {
 	var format string
 	urlPath := r.URL.Path
 	switch filepath.Base(urlPath) {
@@ -31,7 +32,7 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
 	case "archive.tar.bz2":
 		format = "tar.bz2"
 	default:
-		fail500(w, fmt.Errorf("handleGetArchive: invalid format: %s", urlPath))
+		helper.Fail500(w, fmt.Errorf("handleGetArchive: invalid format: %s", urlPath))
 		return
 	}
 
@@ -54,7 +55,7 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
 	// to finalize the cached archive.
 	tempFile, err := prepareArchiveTempfile(path.Dir(a.ArchivePath), archiveFilename)
 	if err != nil {
-		fail500(w, fmt.Errorf("handleGetArchive: create tempfile: %v", err))
+		helper.Fail500(w, fmt.Errorf("handleGetArchive: create tempfile: %v", err))
 		return
 	}
 	defer tempFile.Close()
@@ -65,12 +66,12 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
 	archiveCmd := gitCommand("", "git", "--git-dir="+a.RepoPath, "archive", "--format="+archiveFormat, "--prefix="+a.ArchivePrefix+"/", a.CommitId)
 	archiveStdout, err := archiveCmd.StdoutPipe()
 	if err != nil {
-		fail500(w, fmt.Errorf("handleGetArchive: archive stdout: %v", err))
+		helper.Fail500(w, fmt.Errorf("handleGetArchive: archive stdout: %v", err))
 		return
 	}
 	defer archiveStdout.Close()
 	if err := archiveCmd.Start(); err != nil {
-		fail500(w, fmt.Errorf("handleGetArchive: start %v: %v", archiveCmd.Args, err))
+		helper.Fail500(w, fmt.Errorf("handleGetArchive: start %v: %v", archiveCmd.Args, err))
 		return
 	}
 	defer cleanUpProcessGroup(archiveCmd) // Ensure brute force subprocess clean-up
@@ -83,13 +84,13 @@ func handleGetArchive(w http.ResponseWriter, r *http.Request, a *apiResponse) {
 
 		stdout, err = compressCmd.StdoutPipe()
 		if err != nil {
-			fail500(w, fmt.Errorf("handleGetArchive: compress stdout: %v", err))
+			helper.Fail500(w, fmt.Errorf("handleGetArchive: compress stdout: %v", err))
 			return
 		}
 		defer stdout.Close()
 
 		if err := compressCmd.Start(); err != nil {
-			fail500(w, fmt.Errorf("handleGetArchive: start %v: %v", compressCmd.Args, err))
+			helper.Fail500(w, fmt.Errorf("handleGetArchive: start %v: %v", compressCmd.Args, err))
 			return
 		}
 		defer compressCmd.Wait()
diff --git a/artifacts.go b/artifacts.go
index e1d2dd45b3b6c6a823721b9045db420c959b9010..0a2cfdf4569e76bb426568eb3277552e466e0260 100644
--- a/artifacts.go
+++ b/artifacts.go
@@ -1,11 +1,12 @@
 package main
 
 import (
+	"./internal/api"
 	"net/http"
 )
 
-func artifactsAuthorizeHandler(api *API, h httpHandleFunc) httpHandleFunc {
-	return api.preAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *apiResponse) {
+func artifactsAuthorizeHandler(myAPI *api.API, h http.HandlerFunc) http.HandlerFunc {
+	return myAPI.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
 		r.Header.Set(tempPathHeader, a.TempPath)
 		h(w, r)
 	}, "/authorize")
diff --git a/authorization.go b/authorization.go
deleted file mode 100644
index 8fd7de1f441fb067413365909e516a97fcbdfa3b..0000000000000000000000000000000000000000
--- a/authorization.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package main
-
-import (
-	"./internal/proxy"
-	"encoding/json"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"strings"
-)
-
-func (api *API) newUpstreamRequest(r *http.Request, body io.Reader, suffix string) (*http.Request, error) {
-	url := *api.URL
-	url.Path = r.URL.RequestURI() + suffix
-	authReq := &http.Request{
-		Method: r.Method,
-		URL:    &url,
-		Header: proxy.HeaderClone(r.Header),
-	}
-	if body != nil {
-		authReq.Body = ioutil.NopCloser(body)
-	}
-
-	// Clean some headers when issuing a new request without body
-	if body == nil {
-		authReq.Header.Del("Content-Type")
-		authReq.Header.Del("Content-Encoding")
-		authReq.Header.Del("Content-Length")
-		authReq.Header.Del("Content-Disposition")
-		authReq.Header.Del("Accept-Encoding")
-
-		// Hop-by-hop headers. These are removed when sent to the backend.
-		// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
-		authReq.Header.Del("Transfer-Encoding")
-		authReq.Header.Del("Connection")
-		authReq.Header.Del("Keep-Alive")
-		authReq.Header.Del("Proxy-Authenticate")
-		authReq.Header.Del("Proxy-Authorization")
-		authReq.Header.Del("Te")
-		authReq.Header.Del("Trailers")
-		authReq.Header.Del("Upgrade")
-	}
-
-	// Also forward the Host header, which is excluded from the Header map by the http libary.
-	// This allows the Host header received by the backend to be consistent with other
-	// requests not going through gitlab-workhorse.
-	authReq.Host = r.Host
-	// Set a custom header for the request. This can be used in some
-	// configurations (Passenger) to solve auth request routing problems.
-	authReq.Header.Set("Gitlab-Workhorse", Version)
-
-	return authReq, nil
-}
-
-func (api *API) preAuthorizeHandler(h serviceHandleFunc, suffix string) httpHandleFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		authReq, err := api.newUpstreamRequest(r, nil, suffix)
-		if err != nil {
-			fail500(w, fmt.Errorf("preAuthorizeHandler: newUpstreamRequest: %v", err))
-			return
-		}
-
-		authResponse, err := api.Do(authReq)
-		if err != nil {
-			fail500(w, fmt.Errorf("preAuthorizeHandler: do %v: %v", authReq.URL.Path, err))
-			return
-		}
-		defer authResponse.Body.Close()
-
-		if authResponse.StatusCode != 200 {
-			// The Git request is not allowed by the backend. Maybe the
-			// client needs to send HTTP Basic credentials.  Forward the
-			// response from the auth backend to our client. This includes
-			// the 'WWW-Authenticate' header that acts as a hint that
-			// Basic auth credentials are needed.
-			for k, v := range authResponse.Header {
-				// Accomodate broken clients that do case-sensitive header lookup
-				if k == "Www-Authenticate" {
-					w.Header()["WWW-Authenticate"] = v
-				} else {
-					w.Header()[k] = v
-				}
-			}
-			w.WriteHeader(authResponse.StatusCode)
-			io.Copy(w, authResponse.Body)
-			return
-		}
-
-		a := &apiResponse{}
-		// The auth backend validated the client request and told us additional
-		// request metadata. We must extract this information from the auth
-		// response body.
-		if err := json.NewDecoder(authResponse.Body).Decode(a); err != nil {
-			fail500(w, fmt.Errorf("preAuthorizeHandler: decode authorization response: %v", err))
-			return
-		}
-		// Don't hog a TCP connection in CLOSE_WAIT, we can already close it now
-		authResponse.Body.Close()
-
-		// Negotiate authentication (Kerberos) may need to return a WWW-Authenticate
-		// header to the client even in case of success as per RFC4559.
-		for k, v := range authResponse.Header {
-			// Case-insensitive comparison as per RFC7230
-			if strings.EqualFold(k, "WWW-Authenticate") {
-				w.Header()[k] = v
-			}
-		}
-
-		h(w, r, a)
-	}
-}
diff --git a/authorization_test.go b/authorization_test.go
index a85e3a13986b70b45d6beba4b1d4226844f55229..c2edbb83a8258e0a346eab49fc0f8db26694aec9 100644
--- a/authorization_test.go
+++ b/authorization_test.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"./internal/api"
 	"fmt"
 	"net/http"
 	"net/http/httptest"
@@ -8,7 +9,7 @@ import (
 	"testing"
 )
 
-func okHandler(w http.ResponseWriter, _ *http.Request, _ *apiResponse) {
+func okHandler(w http.ResponseWriter, _ *http.Request, _ *api.Response) {
 	w.WriteHeader(201)
 	fmt.Fprint(w, "{\"status\":\"ok\"}")
 }
@@ -26,7 +27,7 @@ func runPreAuthorizeHandler(t *testing.T, suffix string, url *regexp.Regexp, api
 	api := newUpstream(ts.URL, "").API
 
 	response := httptest.NewRecorder()
-	api.preAuthorizeHandler(okHandler, suffix)(response, httpRequest)
+	api.PreAuthorizeHandler(okHandler, suffix)(response, httpRequest)
 	assertResponseCode(t, response, expectedCode)
 	return response
 }
@@ -35,7 +36,7 @@ func TestPreAuthorizeHappyPath(t *testing.T) {
 	runPreAuthorizeHandler(
 		t, "/authorize",
 		regexp.MustCompile(`/authorize\z`),
-		&apiResponse{},
+		&api.Response{},
 		200, 201)
 }
 
@@ -43,7 +44,7 @@ func TestPreAuthorizeSuffix(t *testing.T) {
 	runPreAuthorizeHandler(
 		t, "/different-authorize",
 		regexp.MustCompile(`/authorize\z`),
-		&apiResponse{},
+		&api.Response{},
 		200, 404)
 }
 
diff --git a/deploy_page.go b/deploy_page.go
index 4db63c6ebb49c512c14b0357eaf043a28cd3a9d6..a1ce2109a2ee3399fd8f74d24b4d97a83c8393c5 100644
--- a/deploy_page.go
+++ b/deploy_page.go
@@ -6,7 +6,7 @@ import (
 	"path/filepath"
 )
 
-func handleDeployPage(documentRoot *string, handler httpHandleFunc) httpHandleFunc {
+func handleDeployPage(documentRoot *string, handler http.HandlerFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		deployPage := filepath.Join(*documentRoot, "index.html")
 		data, err := ioutil.ReadFile(deployPage)
diff --git a/development.go b/development.go
index 17ba95aefda5bb553c58008be655be2ff4cce9bb..eb5f6fdb31ea310ba05dd11bf6b21e13ddad1e7b 100644
--- a/development.go
+++ b/development.go
@@ -2,7 +2,7 @@ package main
 
 import "net/http"
 
-func handleDevelopmentMode(developmentMode *bool, handler httpHandleFunc) httpHandleFunc {
+func handleDevelopmentMode(developmentMode *bool, handler http.HandlerFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		if !*developmentMode {
 			http.NotFound(w, r)
diff --git a/error_pages.go b/error_pages.go
index aa365dd84bf107e7d5932851a00970e3d822d7a5..bd858050fdab20ba9e5d13debbd033a30c34ab43 100644
--- a/error_pages.go
+++ b/error_pages.go
@@ -59,7 +59,7 @@ func (s *errorPageResponseWriter) Flush() {
 	s.WriteHeader(http.StatusOK)
 }
 
-func handleRailsError(documentRoot *string, handler http.Handler) httpHandleFunc {
+func handleRailsError(documentRoot *string, handler http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		rw := errorPageResponseWriter{
 			rw:   w,
diff --git a/error_pages_test.go b/error_pages_test.go
index 44463525578621bdc7351cefe4a359fc66830542..8b78ea3d1d8797aabbd012d8b2a8065861247afb 100644
--- a/error_pages_test.go
+++ b/error_pages_test.go
@@ -21,7 +21,7 @@ func TestIfErrorPageIsPresented(t *testing.T) {
 	ioutil.WriteFile(filepath.Join(dir, "404.html"), []byte(errorPage), 0600)
 
 	w := httptest.NewRecorder()
-	h := httpHandleFunc(func(w http.ResponseWriter, _ *http.Request) {
+	h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
 		w.WriteHeader(404)
 		fmt.Fprint(w, "Not Found")
 	})
@@ -41,7 +41,7 @@ func TestIfErrorPassedIfNoErrorPageIsFound(t *testing.T) {
 
 	w := httptest.NewRecorder()
 	errorResponse := "ERROR"
-	h := httpHandleFunc(func(w http.ResponseWriter, _ *http.Request) {
+	h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
 		w.WriteHeader(404)
 		fmt.Fprint(w, errorResponse)
 	})
diff --git a/git-http.go b/git-http.go
index 48dfe81a528034950325d7ad55fb0793d45d9e45..2364d02ecbb110c84ac85dc6176dc29dc4a0b840 100644
--- a/git-http.go
+++ b/git-http.go
@@ -5,6 +5,7 @@ In this file we handle the Git 'smart HTTP' protocol
 package main
 
 import (
+	"./internal/api"
 	"./internal/helper"
 	"errors"
 	"fmt"
@@ -27,10 +28,10 @@ func looksLikeRepo(p string) bool {
 	return true
 }
 
-func repoPreAuthorizeHandler(api *API, handleFunc serviceHandleFunc) httpHandleFunc {
-	return api.preAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *apiResponse) {
+func repoPreAuthorizeHandler(myAPI *api.API, handleFunc api.HandleFunc) http.HandlerFunc {
+	return myAPI.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
 		if a.RepoPath == "" {
-			fail500(w, errors.New("repoPreAuthorizeHandler: RepoPath empty"))
+			helper.Fail500(w, errors.New("repoPreAuthorizeHandler: RepoPath empty"))
 			return
 		}
 
@@ -43,7 +44,7 @@ func repoPreAuthorizeHandler(api *API, handleFunc serviceHandleFunc) httpHandleF
 	}, "")
 }
 
-func handleGetInfoRefs(w http.ResponseWriter, r *http.Request, a *apiResponse) {
+func handleGetInfoRefs(w http.ResponseWriter, r *http.Request, a *api.Response) {
 	rpc := r.URL.Query().Get("service")
 	if !(rpc == "git-upload-pack" || rpc == "git-receive-pack") {
 		// The 'dumb' Git HTTP protocol is not supported
@@ -55,12 +56,12 @@ func handleGetInfoRefs(w http.ResponseWriter, r *http.Request, a *apiResponse) {
 	cmd := gitCommand(a.GL_ID, "git", subCommand(rpc), "--stateless-rpc", "--advertise-refs", a.RepoPath)
 	stdout, err := cmd.StdoutPipe()
 	if err != nil {
-		fail500(w, fmt.Errorf("handleGetInfoRefs: stdout: %v", err))
+		helper.Fail500(w, fmt.Errorf("handleGetInfoRefs: stdout: %v", err))
 		return
 	}
 	defer stdout.Close()
 	if err := cmd.Start(); err != nil {
-		fail500(w, fmt.Errorf("handleGetInfoRefs: start %v: %v", cmd.Args, err))
+		helper.Fail500(w, fmt.Errorf("handleGetInfoRefs: start %v: %v", cmd.Args, err))
 		return
 	}
 	defer cleanUpProcessGroup(cmd) // Ensure brute force subprocess clean-up
@@ -87,14 +88,14 @@ func handleGetInfoRefs(w http.ResponseWriter, r *http.Request, a *apiResponse) {
 	}
 }
 
-func handlePostRPC(w http.ResponseWriter, r *http.Request, a *apiResponse) {
+func handlePostRPC(w http.ResponseWriter, r *http.Request, a *api.Response) {
 	var err error
 
 	// Get Git action from URL
 	action := filepath.Base(r.URL.Path)
 	if !(action == "git-upload-pack" || action == "git-receive-pack") {
 		// The 'dumb' Git HTTP protocol is not supported
-		fail500(w, fmt.Errorf("handlePostRPC: unsupported action: %s", r.URL.Path))
+		helper.Fail500(w, fmt.Errorf("handlePostRPC: unsupported action: %s", r.URL.Path))
 		return
 	}
 
@@ -102,25 +103,25 @@ func handlePostRPC(w http.ResponseWriter, r *http.Request, a *apiResponse) {
 	cmd := gitCommand(a.GL_ID, "git", subCommand(action), "--stateless-rpc", a.RepoPath)
 	stdout, err := cmd.StdoutPipe()
 	if err != nil {
-		fail500(w, fmt.Errorf("handlePostRPC: stdout: %v", err))
+		helper.Fail500(w, fmt.Errorf("handlePostRPC: stdout: %v", err))
 		return
 	}
 	defer stdout.Close()
 	stdin, err := cmd.StdinPipe()
 	if err != nil {
-		fail500(w, fmt.Errorf("handlePostRPC: stdin: %v", err))
+		helper.Fail500(w, fmt.Errorf("handlePostRPC: stdin: %v", err))
 		return
 	}
 	defer stdin.Close()
 	if err := cmd.Start(); err != nil {
-		fail500(w, fmt.Errorf("handlePostRPC: start %v: %v", cmd.Args, err))
+		helper.Fail500(w, fmt.Errorf("handlePostRPC: start %v: %v", cmd.Args, err))
 		return
 	}
 	defer cleanUpProcessGroup(cmd) // Ensure brute force subprocess clean-up
 
 	// Write the client request body to Git's standard input
 	if _, err := io.Copy(stdin, r.Body); err != nil {
-		fail500(w, fmt.Errorf("handlePostRPC write to %v: %v", cmd.Args, err))
+		helper.Fail500(w, fmt.Errorf("handlePostRPC write to %v: %v", cmd.Args, err))
 		return
 	}
 	// Signal to the Git subprocess that no more data is coming
diff --git a/handlers.go b/handlers.go
index 441e68ae6742ac09adbfa8b076df86fc26f4eee7..ec94c3d23bbc3f4ce8628a36a373df0c7352e34f 100644
--- a/handlers.go
+++ b/handlers.go
@@ -1,13 +1,14 @@
 package main
 
 import (
+	"./internal/helper"
 	"compress/gzip"
 	"fmt"
 	"io"
 	"net/http"
 )
 
-func contentEncodingHandler(h httpHandleFunc) httpHandleFunc {
+func contentEncodingHandler(h http.HandlerFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var body io.ReadCloser
 		var err error
@@ -24,7 +25,7 @@ func contentEncodingHandler(h httpHandleFunc) httpHandleFunc {
 		}
 
 		if err != nil {
-			fail500(w, fmt.Errorf("contentEncodingHandler: %v", err))
+			helper.Fail500(w, fmt.Errorf("contentEncodingHandler: %v", err))
 			return
 		}
 		defer body.Close()
diff --git a/helpers.go b/helpers.go
index c9c75dd8f6f32bef2e0a991dc5d82151f55f9893..5aa9929199ea4a000543042db84481fbb7dca650 100644
--- a/helpers.go
+++ b/helpers.go
@@ -5,7 +5,6 @@ Miscellaneous helpers: logging, errors, subprocesses
 package main
 
 import (
-	"./internal/helper"
 	"fmt"
 	"net/http"
 	"os"
@@ -14,11 +13,6 @@ import (
 	"syscall"
 )
 
-func fail500(w http.ResponseWriter, err error) {
-	http.Error(w, "Internal server error", 500)
-	helper.LogError(err)
-}
-
 func httpError(w http.ResponseWriter, r *http.Request, error string, code int) {
 	if r.ProtoAtLeast(1, 1) {
 		// Force client to disconnect if we render request error
diff --git a/internal/helper/helpers.go b/internal/helper/helpers.go
index 9cf2519577aa9d4f321d0af4da045c717d28cc32..bf2f8a7eed457d1a77202b4d94b70d30f5c1af67 100644
--- a/internal/helper/helpers.go
+++ b/internal/helper/helpers.go
@@ -3,9 +3,15 @@ package helper
 import (
 	"errors"
 	"log"
+	"net/http"
 	"os"
 )
 
+func Fail500(w http.ResponseWriter, err error) {
+	http.Error(w, "Internal server error", 500)
+	LogError(err)
+}
+
 func LogError(err error) {
 	log.Printf("error: %v", err)
 }
diff --git a/lfs.go b/lfs.go
index 63d01e0cff5da121f99b8771b613351a69493d6c..5aa81e466b7962afaa6a4f5e39c5debf66a98b54 100644
--- a/lfs.go
+++ b/lfs.go
@@ -5,6 +5,8 @@ In this file we handle git lfs objects downloads and uploads
 package main
 
 import (
+	"./internal/api"
+	"./internal/helper"
 	"bytes"
 	"crypto/sha256"
 	"encoding/hex"
@@ -17,21 +19,21 @@ import (
 	"path/filepath"
 )
 
-func lfsAuthorizeHandler(api *API, handleFunc serviceHandleFunc) httpHandleFunc {
-	return api.preAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *apiResponse) {
+func lfsAuthorizeHandler(myAPI *api.API, handleFunc api.HandleFunc) http.HandlerFunc {
+	return myAPI.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
 
 		if a.StoreLFSPath == "" {
-			fail500(w, errors.New("lfsAuthorizeHandler: StoreLFSPath empty"))
+			helper.Fail500(w, errors.New("lfsAuthorizeHandler: StoreLFSPath empty"))
 			return
 		}
 
 		if a.LfsOid == "" {
-			fail500(w, errors.New("lfsAuthorizeHandler: LfsOid empty"))
+			helper.Fail500(w, errors.New("lfsAuthorizeHandler: LfsOid empty"))
 			return
 		}
 
 		if err := os.MkdirAll(a.StoreLFSPath, 0700); err != nil {
-			fail500(w, fmt.Errorf("lfsAuthorizeHandler: mkdia StoreLFSPath: %v", err))
+			helper.Fail500(w, fmt.Errorf("lfsAuthorizeHandler: mkdia StoreLFSPath: %v", err))
 			return
 		}
 
@@ -39,11 +41,11 @@ func lfsAuthorizeHandler(api *API, handleFunc serviceHandleFunc) httpHandleFunc
 	}, "/authorize")
 }
 
-func handleStoreLfsObject(h http.Handler) serviceHandleFunc {
-	return func(w http.ResponseWriter, r *http.Request, a *apiResponse) {
+func handleStoreLfsObject(h http.Handler) api.HandleFunc {
+	return func(w http.ResponseWriter, r *http.Request, a *api.Response) {
 		file, err := ioutil.TempFile(a.StoreLFSPath, a.LfsOid)
 		if err != nil {
-			fail500(w, fmt.Errorf("handleStoreLfsObject: create tempfile: %v", err))
+			helper.Fail500(w, fmt.Errorf("handleStoreLfsObject: create tempfile: %v", err))
 			return
 		}
 		defer os.Remove(file.Name())
@@ -54,19 +56,19 @@ func handleStoreLfsObject(h http.Handler) serviceHandleFunc {
 
 		written, err := io.Copy(hw, r.Body)
 		if err != nil {
-			fail500(w, fmt.Errorf("handleStoreLfsObject: write tempfile: %v", err))
+			helper.Fail500(w, fmt.Errorf("handleStoreLfsObject: write tempfile: %v", err))
 			return
 		}
 		file.Close()
 
 		if written != a.LfsSize {
-			fail500(w, fmt.Errorf("handleStoreLfsObject: expected size %d, wrote %d", a.LfsSize, written))
+			helper.Fail500(w, fmt.Errorf("handleStoreLfsObject: expected size %d, wrote %d", a.LfsSize, written))
 			return
 		}
 
 		shaStr := hex.EncodeToString(hash.Sum(nil))
 		if shaStr != a.LfsOid {
-			fail500(w, fmt.Errorf("handleStoreLfsObject: expected sha256 %s, got %s", a.LfsOid, shaStr))
+			helper.Fail500(w, fmt.Errorf("handleStoreLfsObject: expected sha256 %s, got %s", a.LfsOid, shaStr))
 			return
 		}
 
diff --git a/main.go b/main.go
index b4bda1a27efa8a8ce8ae0d2c688a3b6607ab6a66..a29612125306e75ab8371b9ac3458a82e3d3a7e6 100644
--- a/main.go
+++ b/main.go
@@ -46,12 +46,6 @@ type httpRoute struct {
 	handler http.Handler
 }
 
-type httpHandleFunc func(http.ResponseWriter, *http.Request)
-
-func (h httpHandleFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	h(w, r)
-}
-
 const projectPattern = `^/[^/]+/[^/]+/`
 const gitProjectPattern = `^/[^/]+/[^/]+\.git/`
 
diff --git a/main_test.go b/main_test.go
index b6808d65fc51d4960b8a236a462a1d4421b1ac8a..890db645a7ddad3df001fbcbd8bdedd8dddfebf9 100644
--- a/main_test.go
+++ b/main_test.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"./internal/api"
 	"bytes"
 	"encoding/json"
 	"fmt"
@@ -340,7 +341,7 @@ func runOrFail(t *testing.T, cmd *exec.Cmd) {
 }
 
 func gitOkBody(t *testing.T) interface{} {
-	return &apiResponse{
+	return &api.Response{
 		GL_ID:    "user-123",
 		RepoPath: repoPath(t),
 	}
@@ -353,7 +354,7 @@ func archiveOkBody(t *testing.T, archiveName string) interface{} {
 	}
 	archivePath := path.Join(cwd, cacheDir, archiveName)
 
-	return &apiResponse{
+	return &api.Response{
 		RepoPath:      repoPath(t),
 		ArchivePath:   archivePath,
 		CommitId:      "c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd",
diff --git a/servefile.go b/servefile.go
index 7779dd6af1f544b824e21c9ef4337cba0cbbb929..a51428c47890f53354657d77e98f9ca20c253573 100644
--- a/servefile.go
+++ b/servefile.go
@@ -17,13 +17,13 @@ const (
 	CacheExpireMax
 )
 
-func (u *upstream) handleServeFile(documentRoot *string, cache CacheMode, notFoundHandler httpHandleFunc) httpHandleFunc {
+func (u *upstream) handleServeFile(documentRoot *string, cache CacheMode, notFoundHandler http.HandlerFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		file := filepath.Join(*documentRoot, u.relativeURIPath(cleanURIPath(r.URL.Path)))
 
 		// The filepath.Join does Clean traversing directories up
 		if !strings.HasPrefix(file, *documentRoot) {
-			fail500(w, &os.PathError{
+			helper.Fail500(w, &os.PathError{
 				Op:   "open",
 				Path: file,
 				Err:  os.ErrInvalid,
diff --git a/uploads.go b/uploads.go
index 28d5c084f275b1230a3fb4aae8eabc9f7adb665c..dea7e35b80fe3b3d91e1d1ff4219bc77131de631 100644
--- a/uploads.go
+++ b/uploads.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"./internal/helper"
 	"bytes"
 	"errors"
 	"fmt"
@@ -85,11 +86,11 @@ func rewriteFormFilesFromMultipart(r *http.Request, writer *multipart.Writer, te
 	return cleanup, nil
 }
 
-func handleFileUploads(h http.Handler) httpHandleFunc {
+func handleFileUploads(h http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		tempPath := r.Header.Get(tempPathHeader)
 		if tempPath == "" {
-			fail500(w, errors.New("handleFileUploads: TempPath empty"))
+			helper.Fail500(w, errors.New("handleFileUploads: TempPath empty"))
 			return
 		}
 		r.Header.Del(tempPathHeader)
@@ -104,7 +105,7 @@ func handleFileUploads(h http.Handler) httpHandleFunc {
 			if err == http.ErrNotMultipart {
 				h.ServeHTTP(w, r)
 			} else {
-				fail500(w, fmt.Errorf("handleFileUploads: extract files from multipart: %v", err))
+				helper.Fail500(w, fmt.Errorf("handleFileUploads: extract files from multipart: %v", err))
 			}
 			return
 		}
diff --git a/upstream.go b/upstream.go
index 4a557a0f984b5c3f8b96cce043381adedf71fea7..c95b33dd423c246139727e394d3e3cceef4f5603 100644
--- a/upstream.go
+++ b/upstream.go
@@ -7,6 +7,7 @@ In this file we handle request routing and interaction with the authBackend.
 package main
 
 import (
+	"./internal/api"
 	"./internal/proxy"
 	"fmt"
 	"log"
@@ -17,48 +18,13 @@ import (
 	"time"
 )
 
-type serviceHandleFunc func(http.ResponseWriter, *http.Request, *apiResponse)
-
-type API struct {
-	*http.Client
-	*url.URL
-}
-
 type upstream struct {
-	API             *API
+	API             *api.API
 	Proxy           *proxy.Proxy
 	authBackend     string
 	relativeURLRoot string
 }
 
-type apiResponse struct {
-	// GL_ID is an environment variable used by gitlab-shell hooks during 'git
-	// push' and 'git pull'
-	GL_ID string
-	// 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
-	// LFS object id
-	LfsOid string
-	// LFS object size
-	LfsSize int64
-	// TmpPath is the path where we should store temporary files
-	// This is set by authorization middleware
-	TempPath string
-}
-
 func newUpstream(authBackend string, authSocket string) *upstream {
 	parsedURL, err := url.Parse(authBackend)
 	if err != nil {
@@ -88,8 +54,12 @@ func newUpstream(authBackend string, authSocket string) *upstream {
 	proxyTransport := proxy.NewRoundTripper(authTransport)
 
 	up := &upstream{
-		authBackend:     authBackend,
-		API:             &API{Client: &http.Client{Transport: proxyTransport}, URL: parsedURL},
+		authBackend: authBackend,
+		API: &api.API{
+			Client:  &http.Client{Transport: proxyTransport},
+			URL:     parsedURL,
+			Version: Version,
+		},
 		Proxy:           proxy.NewProxy(parsedURL, proxyTransport, Version),
 		relativeURLRoot: relativeURLRoot,
 	}