Merge branch '18076-user-cache-with-repository' into main. Closes #18076
[arvados.git] / services / arv-git-httpd / git_handler.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "log"
9         "net"
10         "net/http"
11         "net/http/cgi"
12         "os"
13
14         "git.arvados.org/arvados.git/sdk/go/arvados"
15 )
16
17 // gitHandler is an http.Handler that invokes git-http-backend (or
18 // whatever backend is configured) via CGI, with appropriate
19 // environment variables in place for git-http-backend or
20 // gitolite-shell.
21 type gitHandler struct {
22         cgi.Handler
23 }
24
25 func newGitHandler(cluster *arvados.Cluster) http.Handler {
26         const glBypass = "GL_BYPASS_ACCESS_CHECKS"
27         const glHome = "GITOLITE_HTTP_HOME"
28         var env []string
29         path := os.Getenv("PATH")
30         if cluster.Git.GitoliteHome != "" {
31                 env = append(env,
32                         glHome+"="+cluster.Git.GitoliteHome,
33                         glBypass+"=1")
34                 path = path + ":" + cluster.Git.GitoliteHome + "/bin"
35         } else if home, bypass := os.Getenv(glHome), os.Getenv(glBypass); home != "" || bypass != "" {
36                 env = append(env, glHome+"="+home, glBypass+"="+bypass)
37                 log.Printf("DEPRECATED: Passing through %s and %s environment variables. Use GitoliteHome configuration instead.", glHome, glBypass)
38         }
39
40         var listen arvados.URL
41         for listen = range cluster.Services.GitHTTP.InternalURLs {
42                 break
43         }
44         env = append(env,
45                 "GIT_PROJECT_ROOT="+cluster.Git.Repositories,
46                 "GIT_HTTP_EXPORT_ALL=",
47                 "SERVER_ADDR="+listen.Host,
48                 "PATH="+path)
49         return &gitHandler{
50                 Handler: cgi.Handler{
51                         Path: cluster.Git.GitCommand,
52                         Dir:  cluster.Git.Repositories,
53                         Env:  env,
54                         Args: []string{"http-backend"},
55                 },
56         }
57 }
58
59 func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
60         remoteHost, remotePort, err := net.SplitHostPort(r.RemoteAddr)
61         if err != nil {
62                 log.Printf("Internal error: SplitHostPort(r.RemoteAddr==%q): %s", r.RemoteAddr, err)
63                 w.WriteHeader(http.StatusInternalServerError)
64                 return
65         }
66
67         // Copy the wrapped cgi.Handler, so these request-specific
68         // variables don't leak into the next request.
69         handlerCopy := h.Handler
70         handlerCopy.Env = append(handlerCopy.Env,
71                 // In Go1.5 we can skip this, net/http/cgi will do it for us:
72                 "REMOTE_HOST="+remoteHost,
73                 "REMOTE_ADDR="+remoteHost,
74                 "REMOTE_PORT="+remotePort,
75                 // Ideally this would be a real username:
76                 "REMOTE_USER="+r.RemoteAddr,
77         )
78         handlerCopy.ServeHTTP(w, r)
79 }