From d65926e34d943bcfe5c951d2a040d02e3f476c9e Mon Sep 17 00:00:00 2001
From: Jacob Vosmaer <contact@jacobvosmaer.nl>
Date: Mon, 29 Feb 2016 11:23:12 +0100
Subject: [PATCH] Buffer git-receive-pack responses

This allows us to respond with HTTP 500 instead of 200 when a 'git
push' is rejected by the server.
---
 internal/git/git-http.go | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/internal/git/git-http.go b/internal/git/git-http.go
index d0c41b8..02144b9 100644
--- a/internal/git/git-http.go
+++ b/internal/git/git-http.go
@@ -7,6 +7,7 @@ package git
 import (
 	"../api"
 	"../helper"
+	"bytes"
 	"errors"
 	"fmt"
 	"io"
@@ -139,18 +140,30 @@ func handlePostRPC(w http.ResponseWriter, r *http.Request, a *api.Response) {
 	// so let's free up some resources already.
 	r.Body.Close()
 
+	bodyWriter := w
+	if action == "git-receive-pack" {
+		// A 'git push' from the client has a small response so it should be OK
+		// to buffer it in memory. If a Git hook on the server rejects the push
+		// it is nice to return a non-200 HTTP status code to the HTTP Git client
+		// so it 'knows' the push was not succesful. Because there is no way (?)
+		// to distinguish between errors (e.g. disk full) and Git hook rejections
+		// the error response from gitlab-workhorse will always be HTTP 500
+		// (Internal Server Error).
+		bodyWriter := &bytes.Buffer{} // buffer the entire response in memory
+		defer flushBuffer(w, bodyWriter)
+	}
+
 	// 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))
+	if _, err := io.Copy(bodyWriter, stdout); err != nil {
+		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
 	}
 }
@@ -168,3 +181,9 @@ func pktFlush(w io.Writer) error {
 	_, err := fmt.Fprint(w, "0000")
 	return err
 }
+
+func flushBuffer(w http.ResponseWriter, buf io.Reader) {
+	if _, err := io.Copy(w, buf); err != nil {
+		helper.Fail500(w, fmt.Errorf("handlePostRPC flush response buffer: %v", err))
+	}
+}
-- 
GitLab