summaryrefslogtreecommitdiff
path: root/workhorse/internal/git/git-http.go
diff options
context:
space:
mode:
Diffstat (limited to 'workhorse/internal/git/git-http.go')
-rw-r--r--workhorse/internal/git/git-http.go100
1 files changed, 100 insertions, 0 deletions
diff --git a/workhorse/internal/git/git-http.go b/workhorse/internal/git/git-http.go
new file mode 100644
index 00000000000..5df20a68bb7
--- /dev/null
+++ b/workhorse/internal/git/git-http.go
@@ -0,0 +1,100 @@
+/*
+In this file we handle the Git 'smart HTTP' protocol
+*/
+
+package git
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "path/filepath"
+ "sync"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+const (
+ // We have to use a negative transfer.hideRefs since this is the only way
+ // to undo an already set parameter: https://www.spinics.net/lists/git/msg256772.html
+ GitConfigShowAllRefs = "transfer.hideRefs=!refs"
+)
+
+func ReceivePack(a *api.API) http.Handler {
+ return postRPCHandler(a, "handleReceivePack", handleReceivePack)
+}
+
+func UploadPack(a *api.API) http.Handler {
+ return postRPCHandler(a, "handleUploadPack", handleUploadPack)
+}
+
+func gitConfigOptions(a *api.Response) []string {
+ var out []string
+
+ if a.ShowAllRefs {
+ out = append(out, GitConfigShowAllRefs)
+ }
+
+ return out
+}
+
+func postRPCHandler(a *api.API, name string, handler func(*HttpResponseWriter, *http.Request, *api.Response) error) http.Handler {
+ return repoPreAuthorizeHandler(a, func(rw http.ResponseWriter, r *http.Request, ar *api.Response) {
+ cr := &countReadCloser{ReadCloser: r.Body}
+ r.Body = cr
+
+ w := NewHttpResponseWriter(rw)
+ defer func() {
+ w.Log(r, cr.Count())
+ }()
+
+ if err := handler(w, r, ar); err != nil {
+ // If the handler already wrote a response this WriteHeader call is a
+ // no-op. It never reaches net/http because GitHttpResponseWriter calls
+ // WriteHeader on its underlying ResponseWriter at most once.
+ w.WriteHeader(500)
+ helper.LogError(r, fmt.Errorf("%s: %v", name, err))
+ }
+ })
+}
+
+func repoPreAuthorizeHandler(myAPI *api.API, handleFunc api.HandleFunc) http.Handler {
+ return myAPI.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
+ handleFunc(w, r, a)
+ }, "")
+}
+
+func writePostRPCHeader(w http.ResponseWriter, action string) {
+ w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-result", action))
+ w.Header().Set("Cache-Control", "no-cache")
+}
+
+func getService(r *http.Request) string {
+ if r.Method == "GET" {
+ return r.URL.Query().Get("service")
+ }
+ return filepath.Base(r.URL.Path)
+}
+
+type countReadCloser struct {
+ n int64
+ io.ReadCloser
+ sync.Mutex
+}
+
+func (c *countReadCloser) Read(p []byte) (n int, err error) {
+ n, err = c.ReadCloser.Read(p)
+
+ c.Lock()
+ defer c.Unlock()
+ c.n += int64(n)
+
+ return n, err
+}
+
+func (c *countReadCloser) Count() int64 {
+ c.Lock()
+ defer c.Unlock()
+ return c.n
+}