--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package githttpd
+
+import (
+ "context"
+ "net"
+ "net/http"
+ "net/http/cgi"
+ "os"
+
+ "git.arvados.org/arvados.git/sdk/go/arvados"
+ "git.arvados.org/arvados.git/sdk/go/ctxlog"
+)
+
+// gitHandler is an http.Handler that invokes git-http-backend (or
+// whatever backend is configured) via CGI, with appropriate
+// environment variables in place for git-http-backend or
+// gitolite-shell.
+type gitHandler struct {
+ cgi.Handler
+}
+
+func newGitHandler(ctx context.Context, cluster *arvados.Cluster) http.Handler {
+ const glBypass = "GL_BYPASS_ACCESS_CHECKS"
+ const glHome = "GITOLITE_HTTP_HOME"
+ var env []string
+ path := os.Getenv("PATH")
+ if cluster.Git.GitoliteHome != "" {
+ env = append(env,
+ glHome+"="+cluster.Git.GitoliteHome,
+ glBypass+"=1")
+ path = path + ":" + cluster.Git.GitoliteHome + "/bin"
+ } else if home, bypass := os.Getenv(glHome), os.Getenv(glBypass); home != "" || bypass != "" {
+ env = append(env, glHome+"="+home, glBypass+"="+bypass)
+ ctxlog.FromContext(ctx).Printf("DEPRECATED: Passing through %s and %s environment variables. Use GitoliteHome configuration instead.", glHome, glBypass)
+ }
+
+ var listen arvados.URL
+ for listen = range cluster.Services.GitHTTP.InternalURLs {
+ break
+ }
+ env = append(env,
+ "GIT_PROJECT_ROOT="+cluster.Git.Repositories,
+ "GIT_HTTP_EXPORT_ALL=",
+ "SERVER_ADDR="+listen.Host,
+ "PATH="+path)
+ return &gitHandler{
+ Handler: cgi.Handler{
+ Path: cluster.Git.GitCommand,
+ Dir: cluster.Git.Repositories,
+ Env: env,
+ Args: []string{"http-backend"},
+ },
+ }
+}
+
+func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ remoteHost, remotePort, err := net.SplitHostPort(r.RemoteAddr)
+ if err != nil {
+ ctxlog.FromContext(r.Context()).Errorf("Internal error: SplitHostPort(r.RemoteAddr==%q): %s", r.RemoteAddr, err)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ // Copy the wrapped cgi.Handler, so these request-specific
+ // variables don't leak into the next request.
+ handlerCopy := h.Handler
+ handlerCopy.Env = append(handlerCopy.Env,
+ // In Go1.5 we can skip this, net/http/cgi will do it for us:
+ "REMOTE_HOST="+remoteHost,
+ "REMOTE_ADDR="+remoteHost,
+ "REMOTE_PORT="+remotePort,
+ // Ideally this would be a real username:
+ "REMOTE_USER="+r.RemoteAddr,
+ )
+ handlerCopy.ServeHTTP(w, r)
+}