Merge branch '8784-dir-listings'
[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
15 // gitHandler is an http.Handler that invokes git-http-backend (or
16 // whatever backend is configured) via CGI, with appropriate
17 // environment variables in place for git-http-backend or
18 // gitolite-shell.
19 type gitHandler struct {
20         cgi.Handler
21 }
22
23 func newGitHandler() http.Handler {
24         const glBypass = "GL_BYPASS_ACCESS_CHECKS"
25         const glHome = "GITOLITE_HTTP_HOME"
26         var env []string
27         path := os.Getenv("PATH")
28         if theConfig.GitoliteHome != "" {
29                 env = append(env,
30                         glHome+"="+theConfig.GitoliteHome,
31                         glBypass+"=1")
32                 path = path + ":" + theConfig.GitoliteHome + "/bin"
33         } else if home, bypass := os.Getenv(glHome), os.Getenv(glBypass); home != "" || bypass != "" {
34                 env = append(env, glHome+"="+home, glBypass+"="+bypass)
35                 log.Printf("DEPRECATED: Passing through %s and %s environment variables. Use GitoliteHome configuration instead.", glHome, glBypass)
36         }
37         env = append(env,
38                 "GIT_PROJECT_ROOT="+theConfig.RepoRoot,
39                 "GIT_HTTP_EXPORT_ALL=",
40                 "SERVER_ADDR="+theConfig.Listen,
41                 "PATH="+path)
42         return &gitHandler{
43                 Handler: cgi.Handler{
44                         Path: theConfig.GitCommand,
45                         Dir:  theConfig.RepoRoot,
46                         Env:  env,
47                         Args: []string{"http-backend"},
48                 },
49         }
50 }
51
52 func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
53         remoteHost, remotePort, err := net.SplitHostPort(r.RemoteAddr)
54         if err != nil {
55                 log.Printf("Internal error: SplitHostPort(r.RemoteAddr==%q): %s", r.RemoteAddr, err)
56                 w.WriteHeader(http.StatusInternalServerError)
57                 return
58         }
59
60         // Copy the wrapped cgi.Handler, so these request-specific
61         // variables don't leak into the next request.
62         handlerCopy := h.Handler
63         handlerCopy.Env = append(handlerCopy.Env,
64                 // In Go1.5 we can skip this, net/http/cgi will do it for us:
65                 "REMOTE_HOST="+remoteHost,
66                 "REMOTE_ADDR="+remoteHost,
67                 "REMOTE_PORT="+remotePort,
68                 // Ideally this would be a real username:
69                 "REMOTE_USER="+r.RemoteAddr,
70         )
71         handlerCopy.ServeHTTP(w, r)
72 }